-
-
Notifications
You must be signed in to change notification settings - Fork 411
New implementation of -profile=gc #1806
Changes from all commits
bdb4939
9b41d5b
6102484
9ffc947
3bec1bf
bde0792
59a53c4
f85c720
dd28709
8d16e58
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| `GC.Stats` provide new metric, total collected memory. | ||
|
|
||
| Result struct of `core.memory.GC.stats()` method now provides additional field, | ||
| `totalCollected`, which is set to total amount of bytes reclaimed by GC during | ||
| collection cycles since the program start. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -187,3 +187,4 @@ template hasElaborateCopyConstructor(T...) | |
| else | ||
| enum bool hasElaborateCopyConstructor = false; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -165,9 +165,12 @@ struct GC | |
| static struct Stats | ||
| { | ||
| /// number of used bytes on the GC heap (might only get updated after a collection) | ||
| size_t usedSize; | ||
| ulong usedSize; | ||
| /// number of free bytes on the GC heap (might only get updated after a collection) | ||
| size_t freeSize; | ||
| ulong freeSize; | ||
| /// number of bytes freed during collections through program lifetime so | ||
| /// far (will count same memory multiple times if re-used) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the point of keeping track of this? It should be mentioned at least in the commit message, but probably also in the changelog and the code docs. |
||
| ulong totalCollected; | ||
| } | ||
|
|
||
| /** | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -49,6 +49,7 @@ import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc; | |
| import core.stdc.string : memcpy, memset, memmove; | ||
| import core.bitop; | ||
| import core.thread; | ||
| import core.sync.mutex; | ||
| static import core.memory; | ||
|
|
||
| version (GNU) import gcc.builtins; | ||
|
|
@@ -115,6 +116,7 @@ __gshared long numFrees; | |
| __gshared long numReallocs; | ||
| __gshared long numExtends; | ||
| __gshared long numOthers; | ||
| __gshared long totalCollectedPages; | ||
| __gshared long mallocTime; // using ticks instead of MonoTime for better performance | ||
| __gshared long freeTime; | ||
| __gshared long reallocTime; | ||
|
|
@@ -251,6 +253,10 @@ debug (LOGGING) | |
|
|
||
| class ConservativeGC : GC | ||
| { | ||
| bool profiling_enabled; | ||
| shared Mutex profiler_lock; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why creating this new mutex instead of using the pre-existing GC global lock for everything?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currenty main GC implementation uses non re-entrant spinlock. Anyway, all lock/sync hacking is more of proof of concept to see if I can make it work that way (have failed so far). |
||
| shared void[__traits(classInstanceSize, Mutex)] profiler_lock_bytes; | ||
|
|
||
| // For passing to debug code (not thread safe) | ||
| __gshared size_t line; | ||
| __gshared char* file; | ||
|
|
@@ -315,6 +321,10 @@ class ConservativeGC : GC | |
| gcx.reserve(config.initReserve << 20); | ||
| if (config.disable) | ||
| gcx.disabled++; | ||
|
|
||
| this.profiler_lock_bytes[0..$] = typeid(Mutex).initializer[]; | ||
| this.profiler_lock = cast(shared(Mutex)) this.profiler_lock_bytes.ptr; | ||
| this.profiler_lock.__ctor(); | ||
| } | ||
|
|
||
|
|
||
|
|
@@ -358,6 +368,13 @@ class ConservativeGC : GC | |
|
|
||
| auto runLocked(alias func, Args...)(auto ref Args args) | ||
| { | ||
| if (this.profiling_enabled) | ||
| { | ||
| this.profiler_lock.lock_nothrow(); | ||
| scope(exit) | ||
| this.profiler_lock.unlock_nothrow(); | ||
| } | ||
|
|
||
| debug(PROFILE_API) immutable tm = (config.profile > 1 ? currTime.ticks : 0); | ||
| lockNR(); | ||
| scope (failure) gcLock.unlock(); | ||
|
|
@@ -1227,6 +1244,17 @@ class ConservativeGC : GC | |
|
|
||
| stats.usedSize -= freeListSize; | ||
| stats.freeSize += freeListSize; | ||
| stats.totalCollected = .totalCollectedPages * PAGESIZE; | ||
| } | ||
|
|
||
| void enableProfiling() nothrow @nogc | ||
| { | ||
| this.profiling_enabled = true; | ||
| } | ||
|
|
||
| shared(Mutex) profilerLock() nothrow @nogc | ||
| { | ||
| return this.profiler_lock; | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -2434,7 +2462,9 @@ struct Gcx | |
|
|
||
| updateCollectThresholds(); | ||
|
|
||
| return freedLargePages + freedSmallPages; | ||
| auto total = freedLargePages + freedSmallPages; | ||
| totalCollectedPages += total; | ||
| return total; | ||
| } | ||
|
|
||
| /** | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,7 +21,7 @@ import core.stdc.string; | |
|
|
||
| import core.exception : onOutOfMemoryError; | ||
|
|
||
| struct Entry { size_t count, size; } | ||
| struct Entry { ulong count, size; } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is changing
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was trying to fix differences in stats between 32-bit and 64-bit builds, stupid idea that will need to be reverted (I have later switched to using two different log files to compare against in test runner). |
||
|
|
||
| char[] buffer; | ||
| Entry[string] newCounts; | ||
|
|
@@ -41,12 +41,15 @@ __gshared | |
|
|
||
| extern (C) void profilegc_setlogfilename(string name) | ||
| { | ||
| import gc.proxy : gc_getProxy; | ||
| gc_getProxy().enableProfiling(); | ||
| logfilename = name; | ||
| } | ||
|
|
||
|
|
||
|
|
||
| public void accumulate(string file, uint line, string funcname, string type, size_t sz) | ||
| public void accumulate(string file, uint line, string funcname, string type, | ||
| ulong sz) | ||
| { | ||
| char[3 * line.sizeof + 1] buf; | ||
| auto buflen = snprintf(buf.ptr, buf.length, "%u", line); | ||
|
|
@@ -85,23 +88,23 @@ public void accumulate(string file, uint line, string funcname, string type, siz | |
| // Merge thread local newCounts into globalNewCounts | ||
| static ~this() | ||
| { | ||
| import gc.proxy : gc_getProxy; | ||
| auto mtx = gc_getProxy().profilerLock(); | ||
|
|
||
| if (newCounts.length) | ||
| { | ||
| synchronized | ||
| mtx.lock(); | ||
| scope(exit) mtx.unlock(); | ||
|
|
||
| foreach (name, entry; newCounts) | ||
| { | ||
| if (globalNewCounts.length) | ||
| { | ||
| // Merge | ||
| foreach (name, entry; newCounts) | ||
| { | ||
| globalNewCounts[name].count += entry.count; | ||
| globalNewCounts[name].size += entry.size; | ||
| } | ||
| } | ||
| else | ||
| // Assign | ||
| globalNewCounts = newCounts; | ||
| if (!(name in globalNewCounts)) | ||
| globalNewCounts[name] = Entry.init; | ||
|
|
||
| globalNewCounts[name].count += entry.count; | ||
| globalNewCounts[name].size += entry.size; | ||
| } | ||
|
|
||
| newCounts = null; | ||
| } | ||
| free(buffer.ptr); | ||
|
|
@@ -121,10 +124,15 @@ shared static ~this() | |
| { | ||
| auto result1 = cast(Result*)r1; | ||
| auto result2 = cast(Result*)r2; | ||
| ptrdiff_t cmp = result2.entry.size - result1.entry.size; | ||
| auto totalSize1 = result1.entry.size * result1.entry.count; | ||
| auto totalSize2 = result2.entry.size * result2.entry.count; | ||
| long cmp = totalSize2 - totalSize1; | ||
| if (cmp) return cmp < 0 ? -1 : 1; | ||
| cmp = result2.entry.size - result1.entry.size; | ||
| if (cmp) return cmp < 0 ? -1 : 1; | ||
| cmp = result2.entry.count - result1.entry.count; | ||
| return cmp < 0 ? -1 : (cmp > 0 ? 1 : 0); | ||
| if (cmp) return cmp < 0 ? -1 : 1; | ||
| return result2.name < result1.name; | ||
| } | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extra newline