2017-06-08 2 views
3

Momentan arbeite ich an dem Schreiben eines C# -Skripts, das in Julia-Modulen geschriebene Funktionen aufrufen kann. Julia bietet eine C-API, mit der Funktionen in Julia aufgerufen werden können. Ich habe es geschafft, Funktionen zu bekommen, die in Julia-Modulen geschrieben wurden, um von C# aus aufgerufen zu werden und Array-Daten zu erhalten, die hin und her gereicht werden.Julia in C# einbetten: Garbage Collector in C# umwandeln Fragen und Fragen

Allerdings bin ich mir nicht ganz sicher, wie man den Garbage Collector korrekt steuert. Dieser Code ist der von julia.h bereitgestellte Inlinecode, der dem Garbage Collector von Julia mitteilt, dass die Variablen, auf die durch Argumente verwiesen wird, in einem anderen Skript verwendet werden und nicht verschoben/freigegeben werden sollten. Jeder Anruf (jl_gc_push() oder jl_gc_push_args() drückt etwas auf den Stapel, die die Garbage Collector verwendet

-Code in julia.h:..

#define jl_pgcstack (jl_get_ptls_states()->pgcstack) 
#define JL_GC_PUSH1(arg1)    \ 
    void *__gc_stkf[] = {(void*)3, jl_pgcstack, arg1};  \ 
    jl_pgcstack = (jl_gcframe_t*)__gc_stkf; 

...(similar functions for 2, 3, 4)............ 

#define JL_GC_PUSH5(arg1, arg2, arg3, arg4, arg5)  \ 
    void *__gc_stkf[] = {(void*)11, jl_pgcstack, arg1, arg2, arg3, arg4, arg5};    \ 
    jl_pgcstack = (jl_gcframe_t*)__gc_stkf; 
#define JL_GC_PUSHARGS(rts_var,n)      \ 
    rts_var = ((jl_value_t**)alloca(((n)+2)*sizeof(jl_value_t*)))+2; \ 
    ((void**)rts_var)[-2] = (void*)(((size_t)(n))<<1);    \ 
    ((void**)rts_var)[-1] = jl_pgcstack;     \ 
    memset((void*)rts_var, 0, (n)*sizeof(jl_value_t*));  \ 
    jl_pgcstack = (jl_gcframe_t*)&(((void**)rts_var)[-2]) 
#define JL_GC_POP() (jl_pgcstack = jl_pgcstack = jl_pgcstack->prev) 

jl_get_ptls_states gibt eine Struktur, die einen Zeiger namens pgcstack Ich glaube, das ist die Sache, die die Garbage Collector verwendet. arg1 sollte vom Typ seine jl_value_t* und rts_var sollte jl_value_t** vom Typ sein.

Frage 1:

ich diesen besonderen Unterschied zwischen dieser Linie in JL_GC_PUSH1 nicht vereinbaren kann (und die anderen JL_GC_PUSH # ist):

void *__gc_stkf[] = {(void*)3, ... 

und diese Linie in JL_GC_PUSHARGS:

((void**)rts_var)[-2] = (void*)(((size_t)(n))<<1); 

Wenn ich JL_GC_PUSH1 verwendet, um dem Garbage Collector mitzuteilen, dass eine Variable ignoriert werden soll, würde die erste Variable im Array auf 3 gesetzt. Wenn ich JL_GC_PUSHARGS verwenden würde, würde sie es auf 2 setzen. Ich dachte Bit Verschiebung nach links gefüllt mit Nullen? Ich verstehe, wie alles andere in diesen Funktionen funktioniert.

Frage 2: Ich schreibe eine C# Funktion das tut, was JL_GC_PUSHARGS tut, außer es in params IntPtr statt jl_value_t** nimmt. Ist es sicher, wenn ich Speicher wie diesen zuteile? Weiß jemand, ob Julia die Zuweisung aufheben wird, oder werde ich Marshal anrufen müssen? FreeHGlobal auf die Erinnerung? Wenn Julia es trotzdem tut und ich Marshal anrufe. Freeh Global, wird es Probleme geben?

C# Version:

public unsafe static void JL_GC_PUSHARGS(params IntPtr[] args) { 
     int l = args.Length; 
     IntPtr* pgcstacknew = (IntPtr*) Marshal.AllocHGlobal(Marshal.SizeOf<IntPtr>() * (l + 2)).ToPointer(); 
     pgcstacknew[0] = (IntPtr)(2 * l + 1); //related to Question 1 
     pgcstacknew[1] = jl_pgcstack(); 
     for(uint i = 2; i < l + 2; i++){ 
      pgcstacknew[i] = args[i - 2]; 
     } 
     jl_pgcstack() = pgcstacknew; 
     //I'm still having issues with this line ^^ 
    } 

Für nimmt jetzt nur, dass jl_pgcstack() auf die Inline-Funktion in C geschrieben entspricht Ich habe Probleme damit, aber das ist ein anderes Problem.

+0

Während es prinzipiell möglich ist, ist das Einhängen direkt in den GC-Stack wahrscheinlich nicht ideal. Eine andere Möglichkeit, mit dem Julia GC zu interagieren, besteht darin, Referenzen in ein globales Array zu pushen. Das macht Pyjulia ([via PyCall] (https://github.com/JuliaPy/PyCall.jl/blob/1d755f27fd440a43b9a792919fee0531495754e0/src/pytype.jl#L433-L440)), und siehe auch die [eng verwandte Diskussion] (https://docs.julaulang.org/en/latest/manual/calling-c-and-fortran-code/#Garbage-Collection-Safety-1) im Hauptteil des Handbuchs. Hoffentlich ist das genug, um loszulegen. –

Antwort

1

Frage 1

Die JL_GC_PUSH1 und JL_GC_PUSHARGS Makros haben unterschiedliche Stapel-Layout. Das niedrige Bit zeigt an, welches es ist.

Frage 2

Julia wird alles nicht freigeben, da soll es nichts zugewiesen werden, wenn ein gc-Rahmen zu schaffen. Wenn Sie zuteilen möchten, ist es normalerweise besser, die Julia-API zu durchlaufen und ein simuliertes Ref-Counting-Schema auf einem ObjectIdDict (jl_eqtable_get/put) zu erstellen.

Eine direkte Übersetzung von JL_GC_PUSHARGS sollte ungefähr so ​​aussehen:

unsafe { 
    // JL_GC_PUSHARGS 
    uint l = args.Length; 
    IntPtr* pgcstacknew = stackalloc IntPtr[l + 2]; 
    pgcstacknew[0] = (IntPtr)(l << 2); // how many roots? 
    pgcstacknew[1] = jl_pgcstack(); // link to previous gc-frame 
    for (uint i = 0; i < l; i++) { // copy the args to the stack roots 
     pgcstacknew[i + 2] = args[i]; 
    } 
    jl_pgcstack() = pgcstacknew; // install frame at top of gc-stack 
} 
// <do stuff with args here> 
unsafe { 
    // JL_GC_POP 
    jl_pgcstack() = pgcstacknew[1]; // remove frame from gc-stack 
} 

Eine weitere Alternative, die jl_call Satz von Funktionen zu verwenden, die den Auf- und Abbau eines gc Rahmen (sowie eine Ausnahmerahmen) enthält .

+0

Ich habe keine so detaillierte Antwort erwartet. Vielen Dank! Ich habe tatsächlich versucht, das zu tun, was Jesaja kommentiert hat, aber wenn das nicht funktioniert, werde ich darauf zurückkommen. –