On 2011-03-03, at 3:20 PM, Bruno Dufour wrote:
AFAIK, v8 is not in our repo, so emailing the patch file to the list works too :)
My changes were "quick and dirty". I added 3 lines to src/heap.cc (look for <=== ADDED):
extern "C" { double bytes_allocated = 0, bytes_alive_at_last_gc = 0; } // <=== ADDED
void Heap::GarbageCollectionPrologue() { double n = SizeOfObjects(); bytes_allocated += n-bytes_alive_at_last_gc; // <=== ADDED TranscendentalCache::Clear(); ClearJSFunctionResultCaches(); ...
void Heap::GarbageCollectionEpilogue() { ... Counters::alive_after_last_gc.Set(static_cast<int>(SizeOfObjects())); bytes_alive_at_last_gc = SizeOfObjects(); // <=== ADDED Counters::symbol_table_capacity.Set(symbol_table()->Capacity()); Counters::number_of_symbols.Set(symbol_table()->NumberOfElements()); #if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING) ReportStatisticsAfterGC(); #endif #ifdef ENABLE_DEBUGGER_SUPPORT Debug::AfterGarbageCollection(); #endif }
And in d8-tachyon-exts.cc I added some functions:
v8::Handlev8::Value v8Proxy_timeCurrentMillis(const v8::Arguments& args) { if (args.Length() != 0) { printf("Error in timeCurrentMillis -- 0 argument expected\n"); exit(1); }
return v8::Number::New(v8::internal::OS::TimeCurrentMillis()); }
extern "C" { extern double bytes_allocated, bytes_alive_at_last_gc; // defined in src/heap.cc }
v8::Handlev8::Value v8Proxy_bytesAllocated(const v8::Arguments& args) { if (args.Length() != 0) { printf("Error in bytesAllocated -- 0 argument expected\n"); exit(1); }
return v8::Number::New(bytes_allocated + (v8::internal::Heap::SizeOfObjects()-bytes_alive_at_last_gc)); }
and in the function init_d8_extensions I added:
global_template->Set( v8::String::New("timeCurrentMillis"), v8::FunctionTemplate::New(v8Proxy_timeCurrentMillis) );
global_template->Set( v8::String::New("bytesAllocated"), v8::FunctionTemplate::New(v8Proxy_bytesAllocated) );
These two functions are used in the implementation of the performance measurement code (in utility/misc.js):
function PerfInfo() { this.time = 0; this.bytes_alloc = 0; this.buckets = {}; }
var perfBuckets = {};
function measurePerformance(bucket, thunk) { var perfInfo;
if (!perfBuckets.hasOwnProperty(bucket)) { perfInfo = new PerfInfo(); perfBuckets[bucket] = perfInfo; } else { perfInfo = perfBuckets[bucket]; }
var perfBucketsOld = perfBuckets;
perfBuckets = perfInfo.buckets;
var start_time = timeCurrentMillis(); var start_bytes_alloc = bytesAllocated();
var result = thunk();
var bytes_alloc = bytesAllocated() - start_bytes_alloc; var time = timeCurrentMillis() - start_time;
perfBuckets = perfBucketsOld;
perfInfo.time += time; perfInfo.bytes_alloc += bytes_alloc;
return result; }
function reportPerformance() { print("******************** Performance report");
for (var bucket in perfBuckets) { var perfInfo = perfBuckets[bucket];
print(""); print(bucket + ": " + perfInfo.time/1000 + " s, " + perfInfo.bytes_alloc/(1024*1024) + " MB allocated");
reportPerformanceSubBuckets(perfInfo, perfInfo.buckets, ""); }
print(""); print("********************"); }
function reportPerformanceSubBuckets(overall, buckets, indent) { indent += " ";
var sortedBuckets = [];
for (var bucket in buckets) sortedBuckets.push(bucket);
sortedBuckets = sortedBuckets.sort( function (x, y) { return buckets[x].time < buckets[y].time; });
for (var i = 0; i<sortedBuckets.length; i++) { var bucket = sortedBuckets[i]; var perfInfo = buckets[bucket];
print(indent + "time=" + Math.floor(perfInfo.time*100/overall.time) + "% " + "alloc=" + Math.floor(perfInfo.bytes_alloc*100/overall.bytes_alloc) + "%" + " -- " + bucket);
reportPerformanceSubBuckets(overall, perfInfo.buckets, indent); } }
That way, wherever some code needs to be monitored for execution time and space usage, all that is needed is a call like:
measurePerformance( "Compiling Tachyon", function () { // Compile the Tachyon sources tachyonIRs = compSources(tachyonSrcs, params); });
and eventually
reportPerformance();
Marc