Question to GC experts: NO_SCAN for a part of the block
Stanislav Blinov via Digitalmars-d
digitalmars-d at puremagic.com
Sat Jun 3 18:08:57 PDT 2017
Suppose I need to allocate several dynamic arrays of different
types and/or sizes. An obvious choice is to do just that: perform
N allocations.
But given that I know all the sizes, and also am pretty sure that
each of the arrays will have the same lifespan, why would I do N
allocations when I can do just one? After all, I can ask the GC
for a sufficiently-sized memory block and put my arrays in it.
But I don't necessarily want the GC to scan *all* my arrays for
pointers. The problem is, with current GC, it seems that it's
impossible to mark only a part of the block as NO_SCAN. Note that
I'm not really talking about precise GC here with full type
information, but about addRange() pointing within a GC-allocated
block. A short example, two arrays, one holding class references,
the other - bytes:
---
import core.memory : GC;
import std.stdio;
import std.typecons;
class C {
int i;
this(int i) {
this.i = i;
}
~this() {
writeln("C(", i, ") dtor");
}
}
auto obvious(int numCs, int numBytes) {
// obvious solution: two arrays, two allocations
return tuple!("Cs", "bytes")(new C[numCs], new
byte[numBytes]);
}
auto scanned(int numCs, int numBytes) {
// one allocation for both arrays,
// for simplicity not dealing with alignment/out of memory
here.
// allocate with default attributes, scanned block
auto memory = GC.malloc(numCs*C.sizeof +
numBytes*byte.sizeof);
auto cs = (cast(C*)memory)[0..numCs];
cs[] = C.init;
auto bytes = (cast(byte*)(memory +
numCs*C.sizeof))[0..numBytes];
bytes[] = byte.init;
return tuple!("Cs", "bytes")(cs, bytes);
}
auto selective(int numCs, int numBytes) {
// one allocation for both arrays,
// for simplicity not dealing with alignment/out of memory
here.
// explicitly ask for NO_SCAN block,
// to not scan bytes for pointers
auto memory = GC.malloc(numCs*C.sizeof + numBytes*byte.sizeof,
GC.BlkAttr.NO_SCAN);
auto cs = (cast(C*)memory)[0..numCs];
cs[] = C.init;
// add scanning range for references
GC.addRange(cs.ptr, cs.length*C.sizeof, typeid(C));
auto bytes = (cast(byte*)(memory +
numCs*C.sizeof))[0..numBytes];
bytes[] = byte.init;
return tuple!("Cs", "bytes")(cs, bytes);
}
void main() {
int numCs = 4; // comes at runtime from elsewhere
int numBytes = 32; // comes at runtime from elsewhere
auto arrays1 = obvious(numCs, numBytes);
int counter;
foreach (ref e; arrays1.Cs)
e = new C(counter++); // dtors will be called
auto arrays2 = scanned(numCs, numBytes);
foreach (ref e; arrays2.Cs)
e = new C(counter++); // dtors will be called
auto arrays3 = selective(numCs, numBytes);
foreach (ref e; arrays3.Cs)
e = new C(counter++); // dtors will not be called
}
---
Should this work, and if not, why?
More information about the Digitalmars-d
mailing list