1 /* 2 A tiny hack to redirect all GC allocations to a fixed size arena. 3 4 Say, you've got an operation that actively allocates in GC'ed heap but 5 after it's complete you don't need anything it allocated. And you need 6 to do it in a loop many times, potentially allocating lots of memory. 7 Just add one line in the beginning of loop's scope and each iteration 8 will reuse the same fixed size buffer. 9 Like this: 10 11 foreach(fname; files) { 12 auto ar = useCleanArena(); // (1) 13 auto img = readPng(fname).getAsTrueColorImage(); 14 process(img); 15 // (2) 16 } 17 18 Between points (1) and (2) all GC allocations will happen inside 19 an arena which will be reused on each iteration. No GC will happen, 20 no garbage accumulated. 21 22 If you need some data created inbetween, you can temporarily pause 23 using the arena and allocate something on main GC heap: 24 25 void testArena() { 26 auto ar = useCleanArena(); 27 auto s = new ubyte[100]; // allocated in arena 28 { 29 auto pause = pauseArena(); // in this scope it's not used 30 auto t = new ubyte[200]; // allocated in main GC heap 31 } 32 auto v = new ubyte[300]; // allocated in arena again 33 writeln("hi"); 34 // end of scope, stop using arena 35 } 36 37 You can set size for arena by calling setArenaSize() before its first use. 38 Default size is 64 MB. 39 40 */ 41 module gcarena; 42 import std.stdio, core.memory, core.exception; 43 44 static if (__VERSION__ < 2066) { 45 static assert(0, "pre 2.066 versions not supported at this moment"); 46 } else { 47 version = D2066; 48 } 49 50 51 void setArenaSize(size_t totalSize) { 52 gcaData.arena_size = totalSize; 53 } 54 55 struct ArenaHandler { 56 @disable this(this); 57 ~this() @nogc { stop(); } 58 59 void stop() @nogc { 60 if (stopped) return; 61 gcaData.clearProxy(); 62 stopped = true; 63 } 64 65 size_t allocated() { return gcaData.arena_pos; } 66 67 private bool stopped = false; 68 } 69 70 auto useCleanArena() { 71 gcaData.arena_pos = 0; 72 gcaData.installProxy(); 73 return ArenaHandler(); 74 } 75 76 auto pauseArena() { 77 gcaData.clearProxy(); 78 struct ArenaPause { 79 ~this() @nogc { gcaData.installProxy(); } 80 @disable this(this); 81 } 82 return ArenaPause(); 83 } 84 85 private: ////////////////////////////////////////////////////////////// 86 alias BlkInfo = GC.BlkInfo; 87 88 struct Proxy // copied from proxy.d (d runtime) 89 { 90 extern (C) 91 { 92 void function() gc_enable; 93 void function() gc_disable; 94 void function() gc_collect; 95 void function() gc_minimize; 96 97 uint function(void*) gc_getAttr; 98 uint function(void*, uint) gc_setAttr; 99 uint function(void*, uint) gc_clrAttr; 100 101 version(D2066) { 102 void* function(size_t, uint, const TypeInfo) gc_malloc; 103 BlkInfo function(size_t, uint, const TypeInfo) gc_qalloc; 104 void* function(size_t, uint, const TypeInfo) gc_calloc; 105 void* function(void*, size_t, uint ba, const TypeInfo) gc_realloc; 106 size_t function(void*, size_t, size_t, const TypeInfo) gc_extend; 107 } else { 108 void* function(size_t, uint) gc_malloc; 109 BlkInfo function(size_t, uint) gc_qalloc; 110 void* function(size_t, uint) gc_calloc; 111 void* function(void*, size_t, uint ba) gc_realloc; 112 size_t function(void*, size_t, size_t) gc_extend; 113 } 114 size_t function(size_t) gc_reserve; 115 void function(void*) gc_free; 116 117 void* function(void*) gc_addrOf; 118 size_t function(void*) gc_sizeOf; 119 120 BlkInfo function(void*) gc_query; 121 122 void function(void*) gc_addRoot; 123 version(D2066) { 124 void function(void*, size_t, const TypeInfo) gc_addRange; 125 } else { 126 void function(void*, size_t) gc_addRange; 127 } 128 129 void function(void*) gc_removeRoot; 130 void function(void*) gc_removeRange; 131 version(D2066) { 132 void function(in void[]) gc_runFinalizers; 133 } 134 } 135 } 136 137 struct GCAData { 138 Proxy myProxy; 139 Proxy *pOrg; // pointer to original Proxy of runtime 140 Proxy** pproxy; 141 142 ubyte[] arena_bytes; 143 size_t arena_pos = 0; 144 size_t arena_size = 64*1024*1024; 145 146 void initProxy() { 147 pOrg = gc_getProxy(); 148 pproxy = cast(Proxy**) (cast(byte*)pOrg + Proxy.sizeof); 149 foreach(funname; __traits(allMembers, Proxy)) 150 __traits(getMember, myProxy, funname) = &genCall!funname; 151 myProxy.gc_malloc = &gca_malloc; 152 myProxy.gc_qalloc = &gca_qalloc; 153 myProxy.gc_calloc = &gca_calloc; 154 } 155 156 void* alloc(size_t size) { 157 if (arena_bytes.length==0) { 158 auto oldproxy = *pproxy; 159 *pproxy = null; 160 arena_bytes = new ubyte[arena_size]; 161 *pproxy = oldproxy; 162 } 163 164 if (arena_pos + size > arena_bytes.length) { 165 writeln("Arena too small! arena=", arena_bytes.length, " asked for ", size, " need ", arena_pos + size); 166 onOutOfMemoryError(); 167 } 168 auto pos = arena_pos; 169 arena_pos += size; 170 arena_pos = (arena_pos + 15) & ~15; 171 return &arena_bytes[pos]; 172 } 173 174 void clearArena() { 175 writeln("clearArena: allocated ", arena_pos); 176 arena_pos = 0; 177 } 178 179 void installProxy() @nogc { 180 // writeln("using arena now"); 181 *pproxy = &myProxy; 182 } 183 184 void clearProxy() @nogc { 185 // writeln("using GC now"); 186 *pproxy = null; 187 } 188 } 189 190 extern(C) { 191 Proxy* gc_getProxy(); 192 193 auto genCall(string funname)(FunArgsTypes!funname args) { 194 *gcaData.pproxy = null; 195 scope(exit) *gcaData.pproxy = &gcaData.myProxy; 196 return __traits(getMember, *gcaData.pOrg, funname)(args); 197 } 198 199 void* gca_malloc(size_t sz, uint ba, const TypeInfo ti) { 200 //writeln("gca_malloc ", sz); 201 return gcaData.alloc(sz); 202 } 203 204 BlkInfo gca_qalloc(size_t sz, uint ba, const TypeInfo ti) { 205 //writeln("gca_qalloc ", sz); 206 auto pos0 = gcaData.arena_pos; 207 BlkInfo b; 208 b.base = gcaData.alloc(sz); 209 b.size = gcaData.arena_pos - pos0; 210 b.attr = ba; 211 return b; 212 } 213 214 void* gca_calloc(size_t sz, uint ba, const TypeInfo ti) { 215 import core.stdc.string : memset; 216 //writeln("gca_calloc ", sz); 217 void* p = gcaData.alloc(sz); 218 memset(p, 0, sz); 219 return p; 220 } 221 } 222 223 template FunArgsTypes(string funname) { 224 alias FunType = typeof(*__traits(getMember, gcaData.myProxy, funname)); 225 alias FunArgsTypes = ParameterTypeTuple!FunType; 226 } 227 228 GCAData gcaData; // thread local 229 230 static this() { 231 gcaData.initProxy(); 232 }