Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SIGSEGV: Illegal storage access. (Attempt to read from nil?) when using nim-2.2.0 and nim-2.0.10 but it works in nim-1.6.20 and nim-2.0.8 #24242

Open
forchid opened this issue Oct 5, 2024 · 4 comments

Comments

@forchid
Copy link

forchid commented Oct 5, 2024

Description

Test source

# Very slow ... but very quick in nim-2.0.8(1.6.20 best)!
#import std/lists
import std/os
#import std/strformat
import strutils

type
  ObjRef = ref object of RootObj
    data: int
    remark: string
    parent: ObjRef
    children: seq[ObjRef]
   
proc test(o: ObjRef, i: int) =
    #echo "data = ", o.data
    var o = new(ObjRef)
    o.data = i
    #o.remark = fmt"*ObjRef*#{i}Foo-bar-foo-bar-foo-bar#{i}"
    var c = ObjRef(data: i + 1)
    o.children.add(c)
    c.parent = o
    o.children = @[]
  
proc printDiagnostics() =
  echo("Total memory available: " & formatSize(getTotalMem()) & " bytes")
  echo("Free memory: " & formatSize(getFreeMem()) & " bytes")
  echo("Occupied memory: " & formatSize(getOccupiedMem()) & " bytes")
   
proc main =
    var
        o: ObjRef
        #i = 0
        n = 100000000
        #a = initSinglyLinkedList[ObjRef]()
        a = newSeq[ObjRef]()

    #while i <= n:
    for i in 1 .. n + 1:
        #inc i
        o = new(ObjRef)
        o.data = i
        #o.remark = fmt"ObjRef#{i}Foo-bar-foo-bar-foo-bar#{i}"
        var c = ObjRef(data: i + 1)
        o.children.add(c)
        c.parent = o;
        o.test(i)
        a.add(o)
  
        if i mod 1000_0000 == 0:
            #a = initSinglyLinkedList[ObjRef]()
            for item in a: item.children = @[]
            a = @[]
  
        if i mod 10000000 == 0:
            echo "i = ", i
            o.test(i)
            printDiagnostics();

main()

const STM = 1000
for i in 1 .. 1800:
    printDiagnostics();
    echo $i & ". Sleep " & $STM & "ms"
    sleep(STM)
echo "Bye!"

Nim Version

%nim22_home%\bin\nim -v
Nim Compiler Version 2.2.0 [Windows: amd64]
Compiled at 2024-10-02
Copyright (c) 2006-2024 by Andreas Rumpf

active boot switches: -d:release

Current Output

>%nim22_home%\bin\nim c -d:release objtest.nim
Hint: used config file '...\nim-2.2.0\config\nim.cfg' [Conf]
Hint: used config file '...\nim-2.2.0\config\config.nims' [Conf]
.....................................................................................................................
Hint:  [Link]
Hint: mm: orc; threads: on; opt: speed; options: -d:release
51928 lines; 0.953s; 73.387MiB peakmem; proj: ...\objtest.nim; out: ...\objtest.exe [SuccessX]

>objtest
i = 10000000
Total memory available: 2.363GiB bytes
Free memory: 526.309MiB bytes
Occupied memory: 176MiB bytes
SIGSEGV: Illegal storage access. (Attempt to read from nil?)

>echo %errorlevel%
1

Expected Output

>objtest
i = 10000000
Total memory available: 2.363GiB bytes
Free memory: 526.309MiB bytes
Occupied memory: 176MiB bytes
i = 20000000
...

Known Workarounds

No response

Additional Information

No response

@tersec
Copy link
Contributor

tersec commented Oct 5, 2024

Seems ORC-only. refc doesn't seem affected.

@metagn
Copy link
Collaborator

metagn commented Oct 5, 2024

This code is basically a pure stress test of the cycle collector. The segfault is probably a legitimate issue but there are some tips I would give to improve the performance on ORC, and do work around this issue.

  • Mark parent as {.cursor.}. This alone works around the issue, but it's still not super fast and the occupied memory caps at exactly 400 MiB. Doing this along with marking ObjRef as {.acyclic.} drops the occupied memory to 320 bytes. Weirdly enough, not using {.cursor.} but still marking as {.acyclic.}/using ARC also works, but it would probably segfault if parent was read from.
  • If the object is unavoidably cyclic, i.e. we can't use {.cursor.}, disable the cycle collector via GC_disableOrc() and only run it a few times during the loop via GC_runOrc(). This gives worse performance than the above but also works around the issue.

The fact that ObjRef uses inheritance isn't related.

@forchid
Copy link
Author

forchid commented Oct 5, 2024

  • Weirdly enough, not using {.cursor.} but still marking as {.acyclic.}/using ARC also works,

@metagn This test actually hasn't cyclic refs for these cyclic relationships broken manually.

@metagn
Copy link
Collaborator

metagn commented Oct 5, 2024

The parent and children are mutually cyclic with each other. If the parent is not marked {.cursor.}, when the child is destroyed, the parent is destroyed as well. This normally causes issues but it's not encountered in this test, if that's what you meant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants