Search code examples
javascriptoptimizationv8micro-optimization

JavaScript not performing consistently (V8)


I have the following JavaScript, which performs a Tournament selection among a group of scores.

// Benchmark Tests
benchmark( 1e2 );
benchmark( 1e3 );
benchmark( 1e4 );
benchmark( 1e6 );

benchmark( 1e4, 50, 20 );

// Selection Function
function tournamentSelection( population, size, probability ) {
  var participants = [];
  for( var i = 0; i < size; i++ ) {
    var selected = population[ Math.random() * population.length << 0 ];
    participants.push( selected );
  }
  
  participants.sort( ( a, b ) => b.Score - a.Score );
  for( var i = 0; i < size; i++ )
    if ( Math.random() < probability | i === size - 1 )
      return participants[i];
}

// Benchmark Function
function benchmark( iterations, populationSize, tournamentSize, probability ) {
  iterations = iterations || 1e3;
  populationSize = populationSize || 50;
  tournamentSize = tournamentSize || 5;
  probability = probability || 0.75;
  
  var population = [];
  while( populationSize-- )
    population.push( { Score: Math.random() * 1e6 } );
   
  var start = performance.now();
  for( var i = 0; i < iterations; i++ ) {
    var selected = tournamentSelection( population, tournamentSize, probability )
  }
  var end = performance.now();
  
  var total = end - start;
  var avgInNS = ( total * 1e6 ) / iterations;
  
  console.log( iterations 
            + ' iterations took '
            + total.toFixed( 3 )
            + 'ms ('
            + avgInNS.toFixed( 3 )
            + 'ns avg. per iteration)' );
}

I have noticed that as iterations increase, the average execution time decreases. I believe this to be the doing of V8's runtime optimization, but in regard to exactly how it is being optimized, no clues are given. To add to the mystery, when running it in NodeJS with flag --allow-natives-syntax and %NeverOptimizeFunction(tournamentSelection); specified, the differing performance is not fixed.

My goal is to optimize this selection algorithm to perform more consistently. I assume that the call to sort() is the most expensive operation included, as increasing the tournament size has a massive impact on performance. However, if that is being optimized behind-the-scenes, I'm not really given any options, as V8 implements that natively with quicksort.

Does anyone have an idea of what V8 is doing? Also, any suggestion to improve the algorithm's performance would be much appreciated.

Edit: Here's a profiling log of running test case #2 in a NodeJS environment:

Statistical profiling result from isolate-0x3578c70-v8.log, (73 ticks, 0 unaccounted, 0 excluded).

 [Shared libraries]:
   ticks  total  nonlib   name
      5    6.8%          /usr/bin/node
      1    1.4%          /lib/x86_64-linux-gnu/libc-2.27.so

 [JavaScript]:
   ticks  total  nonlib   name
      1    1.4%    1.5%  LazyCompile: ~participants.sort /home/haus/Desktop/Bostrom/Genetics/Selection.js:82:22
      1    1.4%    1.5%  LazyCompile: module.exports.Tournament /home/haus/Desktop/Bostrom/Genetics/Selection.js:67:38

 [C++]:
   ticks  total  nonlib   name
     23   31.5%   34.3%  node::contextify::ContextifyScript::New(v8::FunctionCallbackInfo<v8::Value> const&)
     16   21.9%   23.9%  write
      2    2.7%    3.0%  v8::internal::Zone::New(unsigned long)
      2    2.7%    3.0%  mprotect
      1    1.4%    1.5%  void v8::internal::Scanner::Advance<false, true>()
      1    1.4%    1.5%  v8::internal::interpreter::BytecodeRegisterOptimizer::RegisterTransfer(v8::internal::interpreter::BytecodeRegisterOptimizer::RegisterInfo*, v8::internal::interpreter::BytecodeRegisterOptimizer::RegisterInfo*)
      1    1.4%    1.5%  v8::internal::interpreter::BytecodeRegisterOptimizer::Flush()
      1    1.4%    1.5%  v8::internal::interpreter::BytecodeGenerator::VisitAssignment(v8::internal::Assignment*)
      1    1.4%    1.5%  v8::internal::interpreter::BytecodeArrayBuilder::StackCheck(int)
      1    1.4%    1.5%  v8::internal::compiler::LiveRangeConnector::ConnectRanges(v8::internal::Zone*)
      1    1.4%    1.5%  v8::internal::compiler::CodeAssembler::ChangeInt32ToIntPtr(v8::internal::compiler::SloppyTNode<v8::internal::Word32T>)
      1    1.4%    1.5%  v8::internal::StackFrame::GetCallerState(v8::internal::StackFrame::State*) const
      1    1.4%    1.5%  v8::internal::Scanner::Scanner(v8::internal::UnicodeCache*)
      1    1.4%    1.5%  v8::internal::HashTable<v8::internal::StringTable, v8::internal::StringTableShape>::Rehash()
      1    1.4%    1.5%  v8::internal::HashTable<v8::internal::GlobalDictionary, v8::internal::GlobalDictionaryShape>::EnsureCapacity(v8::internal::Handle<v8::internal::GlobalDictionary>, int, v8::internal::PretenureFlag)
      1    1.4%    1.5%  v8::internal::Factory::NewFeedbackVector(v8::internal::Handle<v8::internal::SharedFunctionInfo>, v8::internal::PretenureFlag)
      1    1.4%    1.5%  v8::internal::Deserializer<v8::internal::DefaultDeserializerAllocator>::ReadData(v8::internal::MaybeObject**, v8::internal::MaybeObject**, int, unsigned char*)
      1    1.4%    1.5%  v8::internal::DeclarationScope::AllocateScopeInfos(v8::internal::ParseInfo*, v8::internal::Isolate*, v8::internal::AnalyzeMode)
      1    1.4%    1.5%  v8::internal::CompilationCacheTable::PutRegExp(v8::internal::Handle<v8::internal::CompilationCacheTable>, v8::internal::Handle<v8::internal::String>, v8::base::Flags<v8::internal::JSRegExp::Flag, int>, v8::internal::Handle<v8::internal::FixedArray>)
      1    1.4%    1.5%  v8::internal::BufferedUtf16CharacterStream::ReadBlock()
      1    1.4%    1.5%  v8::internal::AstRawString::AsArrayIndex(unsigned int*) const
      1    1.4%    1.5%  std::ostream::sentry::sentry(std::ostream&)
      1    1.4%    1.5%  do_futex_wait.constprop.1
      1    1.4%    1.5%  cfree
      1    1.4%    1.5%  _dl_addr
      1    1.4%    1.5%  _IO_file_sync

 [Summary]:
   ticks  total  nonlib   name
      2    2.7%    3.0%  JavaScript
     65   89.0%   97.0%  C++
      2    2.7%    3.0%  GC
      6    8.2%          Shared libraries

 [C++ entry points]:
   ticks    cpp   total   name
     25   43.9%   34.2%  v8::internal::Builtin_HandleApiCall(int, v8::internal::Object**, v8::internal::Isolate*)
     18   31.6%   24.7%  v8::internal::Runtime_CompileLazy(int, v8::internal::Object**, v8::internal::Isolate*)
      5    8.8%    6.8%  v8::internal::Runtime_InterpreterDeserializeLazy(int, v8::internal::Object**, v8::internal::Isolate*)
      2    3.5%    2.7%  v8::internal::Runtime_KeyedStoreIC_Miss(int, v8::internal::Object**, v8::internal::Isolate*)
      2    3.5%    2.7%  v8::internal::Runtime_DeserializeLazy(int, v8::internal::Object**, v8::internal::Isolate*)
      1    1.8%    1.4%  v8::internal::Runtime_NewClosure(int, v8::internal::Object**, v8::internal::Isolate*)
      1    1.8%    1.4%  v8::internal::Runtime_LoadIC_Miss(int, v8::internal::Object**, v8::internal::Isolate*)
      1    1.8%    1.4%  v8::internal::Runtime_DefineClass(int, v8::internal::Object**, v8::internal::Isolate*)
      1    1.8%    1.4%  v8::internal::Runtime_CreateRegExpLiteral(int, v8::internal::Object**, v8::internal::Isolate*)
      1    1.8%    1.4%  v8::internal::Builtin_ObjectDefineProperties(int, v8::internal::Object**, v8::internal::Isolate*)

 [Bottom up (heavy) profile]:
  Note: percentage shows a share of a particular caller in the total
  amount of its parent calls.
  Callers occupying less than 1.0% are not shown.

   ticks parent  name
     23   31.5%  node::contextify::ContextifyScript::New(v8::FunctionCallbackInfo<v8::Value> const&)
     23  100.0%    v8::internal::Builtin_HandleApiCall(int, v8::internal::Object**, v8::internal::Isolate*)
     23  100.0%      LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44
     23  100.0%        LazyCompile: ~NativeModule.require internal/bootstrap/loaders.js:137:34
      3   13.0%          Script: ~<anonymous> fs.js:1:11
      3  100.0%            LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44
      3   13.0%          Script: ~<anonymous> assert.js:1:11
      3  100.0%            LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44
      2    8.7%          Script: ~<anonymous> util.js:1:11
      2  100.0%            LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44
      2    8.7%          Script: ~<anonymous> stream.js:1:11
      2  100.0%            LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44
      2    8.7%          LazyCompile: ~startup internal/bootstrap/node.js:29:19
      2  100.0%            Script: ~bootstrapNodeJSCore internal/bootstrap/node.js:15:30
      1    4.3%          Script: ~<anonymous> tty.js:1:11
      1  100.0%            LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44
      1    4.3%          Script: ~<anonymous> perf_hooks.js:1:11
      1  100.0%            LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44
      1    4.3%          Script: ~<anonymous> net.js:1:11
      1  100.0%            LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44
      1    4.3%          Script: ~<anonymous> internal/worker.js:1:11
      1  100.0%            LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44
      1    4.3%          Script: ~<anonymous> internal/process.js:1:11
      1  100.0%            LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44
      1    4.3%          Script: ~<anonymous> internal/modules/cjs/loader.js:1:11
      1  100.0%            LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44
      1    4.3%          Script: ~<anonymous> internal/async_hooks.js:1:11
      1  100.0%            LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44
      1    4.3%          LazyCompile: ~setupProcessFatal internal/bootstrap/node.js:420:29
      1  100.0%            LazyCompile: ~startup internal/bootstrap/node.js:29:19
      1    4.3%          LazyCompile: ~setupNextTick internal/process/next_tick.js:5:23
      1  100.0%            LazyCompile: ~startup internal/bootstrap/node.js:29:19
      1    4.3%          LazyCompile: ~setupGlobalVariables internal/bootstrap/node.js:293:32
      1  100.0%            LazyCompile: ~startup internal/bootstrap/node.js:29:19
      1    4.3%          LazyCompile: ~setupGlobalTimeouts internal/bootstrap/node.js:342:31
      1  100.0%            LazyCompile: ~startup internal/bootstrap/node.js:29:19

     16   21.9%  write
      5   31.3%    v8::internal::Runtime_CompileLazy(int, v8::internal::Object**, v8::internal::Isolate*)
      1   20.0%      LazyCompile: ~tryExtensions internal/modules/cjs/loader.js:208:23
      1  100.0%        LazyCompile: ~Module._findPath internal/modules/cjs/loader.js:220:28
      1  100.0%          LazyCompile: ~Module._resolveFilename internal/modules/cjs/loader.js:547:35
      1  100.0%            LazyCompile: ~Module._load internal/modules/cjs/loader.js:502:24
      1   20.0%      LazyCompile: ~setupGlobalConsole internal/bootstrap/node.js:352:30
      1  100.0%        LazyCompile: ~startup internal/bootstrap/node.js:29:19
      1  100.0%          Script: ~bootstrapNodeJSCore internal/bootstrap/node.js:15:30
      1   20.0%      LazyCompile: ~readFileSync fs.js:345:22
      1  100.0%        LazyCompile: ~Module._extensions..js internal/modules/cjs/loader.js:698:37
      1  100.0%          LazyCompile: ~Module.load internal/modules/cjs/loader.js:590:33
      1  100.0%            LazyCompile: ~tryModuleLoad internal/modules/cjs/loader.js:535:23
      1   20.0%      LazyCompile: ~getStdout internal/process/stdio.js:21:21
      1  100.0%        Script: ~<anonymous> console.js:1:11
      1  100.0%          LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44
      1  100.0%            LazyCompile: ~NativeModule.require internal/bootstrap/loaders.js:137:34
      1   20.0%      LazyCompile: ~Socket net.js:221:16
      1  100.0%        LazyCompile: ~WriteStream tty.js:70:21
      1  100.0%          LazyCompile: ~createWritableStdioStream internal/process/stdio.js:163:35
      1  100.0%            LazyCompile: ~getStdout internal/process/stdio.js:21:21
      3   18.8%    v8::internal::Runtime_InterpreterDeserializeLazy(int, v8::internal::Object**, v8::internal::Isolate*)
      1   33.3%      Script: ~<anonymous> :5:10
      1  100.0%        Script: ~<anonymous> :1:1
      1   33.3%      LazyCompile: ~binding internal/bootstrap/loaders.js:77:39
      1  100.0%        Script: ~bootstrapInternalLoaders internal/bootstrap/loaders.js:42:35
      1   33.3%      LazyCompile: ~_extend util.js:1231:17
      1  100.0%        LazyCompile: ~Socket net.js:221:16
      1  100.0%          LazyCompile: ~WriteStream tty.js:70:21
      1  100.0%            LazyCompile: ~createWritableStdioStream internal/process/stdio.js:163:35
      1    6.3%    v8::internal::Runtime_DeserializeLazy(int, v8::internal::Object**, v8::internal::Isolate*)
      1  100.0%      Script: ~<anonymous> internal/util/types.js:1:11
      1  100.0%        LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44
      1  100.0%          LazyCompile: ~NativeModule.require internal/bootstrap/loaders.js:137:34
      1  100.0%            Script: ~<anonymous> internal/encoding.js:1:11

      5    6.8%  /usr/bin/node
      2   40.0%    v8::internal::Builtin_HandleApiCall(int, v8::internal::Object**, v8::internal::Isolate*)
      2  100.0%      LazyCompile: ~binding internal/bootstrap/loaders.js:77:39
      1   50.0%        Script: ~<anonymous> internal/util.js:1:11
      1  100.0%          LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44
      1  100.0%            LazyCompile: ~NativeModule.require internal/bootstrap/loaders.js:137:34
      1   50.0%        LazyCompile: ~setupProcessICUVersions internal/bootstrap/node.js:474:35
      1  100.0%          LazyCompile: ~startup internal/bootstrap/node.js:29:19
      1  100.0%            Script: ~bootstrapNodeJSCore internal/bootstrap/node.js:15:30
      1   20.0%    v8::internal::Runtime_DefineClass(int, v8::internal::Object**, v8::internal::Isolate*)
      1  100.0%      Script: ~<anonymous> internal/worker.js:1:11
      1  100.0%        LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44
      1  100.0%          LazyCompile: ~NativeModule.require internal/bootstrap/loaders.js:137:34
      1  100.0%            Script: ~<anonymous> internal/process.js:1:11
      1   20.0%    v8::internal::Runtime_CompileLazy(int, v8::internal::Object**, v8::internal::Isolate*)
      1  100.0%      LazyCompile: ~emitBeforeScript internal/async_hooks.js:340:26
      1  100.0%        LazyCompile: ~_tickCallback internal/process/next_tick.js:41:25
      1  100.0%          LazyCompile: ~Module.runMain internal/modules/cjs/loader.js:729:26
      1  100.0%            LazyCompile: ~startup internal/bootstrap/node.js:29:19

      2    2.7%  v8::internal::Zone::New(unsigned long)
      2  100.0%    v8::internal::Runtime_CompileLazy(int, v8::internal::Object**, v8::internal::Isolate*)
      1   50.0%      LazyCompile: ~startup internal/bootstrap/node.js:29:19
      1  100.0%        Script: ~bootstrapNodeJSCore internal/bootstrap/node.js:15:30
      1   50.0%      LazyCompile: ~emitAfterScript internal/async_hooks.js:354:25
      1  100.0%        LazyCompile: ~_tickCallback internal/process/next_tick.js:41:25
      1  100.0%          LazyCompile: ~Module.runMain internal/modules/cjs/loader.js:729:26
      1  100.0%            LazyCompile: ~startup internal/bootstrap/node.js:29:19

      2    2.7%  mprotect
      2  100.0%    v8::internal::Runtime_InterpreterDeserializeLazy(int, v8::internal::Object**, v8::internal::Isolate*)
      1   50.0%      LazyCompile: ~NativeModule.require internal/bootstrap/loaders.js:137:34
      1  100.0%        LazyCompile: ~startup internal/bootstrap/node.js:29:19
      1  100.0%          Script: ~bootstrapNodeJSCore internal/bootstrap/node.js:15:30
      1   50.0%      LazyCompile: module.exports.Tournament /home/haus/Desktop/Bostrom/Genetics/Selection.js:67:38
      1  100.0%        Script: ~<anonymous> /home/haus/Desktop/Bostrom/test.js:1:11
      1  100.0%          LazyCompile: ~Module._compile internal/modules/cjs/loader.js:650:37
      1  100.0%            LazyCompile: ~Module._extensions..js internal/modules/cjs/loader.js:698:37

      1    1.4%  void v8::internal::Scanner::Advance<false, true>()
      1  100.0%    v8::internal::Runtime_CompileLazy(int, v8::internal::Object**, v8::internal::Isolate*)
      1  100.0%      LazyCompile: ~formatValue util.js:420:21
      1  100.0%        LazyCompile: ~inspect util.js:291:17
      1  100.0%          LazyCompile: ~formatWithOptions util.js:173:27
      1  100.0%            LazyCompile: ~Console.(anonymous function) console.js:186:47

      1    1.4%  v8::internal::interpreter::BytecodeRegisterOptimizer::RegisterTransfer(v8::internal::interpreter::BytecodeRegisterOptimizer::RegisterInfo*, v8::internal::interpreter::BytecodeRegisterOptimizer::RegisterInfo*)
      1  100.0%    v8::internal::Runtime_CompileLazy(int, v8::internal::Object**, v8::internal::Isolate*)
      1  100.0%      LazyCompile: ~InnerArraySort native array.js:487:24
      1  100.0%        LazyCompile: ~sort native array.js:708:46
      1  100.0%          Script: ~<anonymous> internal/modules/cjs/helpers.js:1:11
      1  100.0%            LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44

      1    1.4%  v8::internal::interpreter::BytecodeRegisterOptimizer::Flush()
      1  100.0%    v8::internal::Runtime_CompileLazy(int, v8::internal::Object**, v8::internal::Isolate*)
      1  100.0%      LazyCompile: ~Module._load internal/modules/cjs/loader.js:502:24
      1  100.0%        LazyCompile: ~Module.runMain internal/modules/cjs/loader.js:729:26
      1  100.0%          LazyCompile: ~startup internal/bootstrap/node.js:29:19
      1  100.0%            Script: ~bootstrapNodeJSCore internal/bootstrap/node.js:15:30

      1    1.4%  v8::internal::interpreter::BytecodeGenerator::VisitAssignment(v8::internal::Assignment*)
      1  100.0%    v8::internal::Runtime_CompileLazy(int, v8::internal::Object**, v8::internal::Isolate*)
      1  100.0%      Script: ~<anonymous> internal/url.js:1:11
      1  100.0%        LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44
      1  100.0%          LazyCompile: ~NativeModule.require internal/bootstrap/loaders.js:137:34
      1  100.0%            Script: ~<anonymous> fs.js:1:11

      1    1.4%  v8::internal::interpreter::BytecodeArrayBuilder::StackCheck(int)
      1  100.0%    v8::internal::Runtime_CompileLazy(int, v8::internal::Object**, v8::internal::Isolate*)
      1  100.0%      LazyCompile: ~readFileSync fs.js:345:22
      1  100.0%        LazyCompile: ~Module._extensions..js internal/modules/cjs/loader.js:698:37
      1  100.0%          LazyCompile: ~Module.load internal/modules/cjs/loader.js:590:33
      1  100.0%            LazyCompile: ~tryModuleLoad internal/modules/cjs/loader.js:535:23

      1    1.4%  v8::internal::compiler::LiveRangeConnector::ConnectRanges(v8::internal::Zone*)
      1  100.0%    v8::internal::Runtime_KeyedStoreIC_Miss(int, v8::internal::Object**, v8::internal::Isolate*)
      1  100.0%      LazyCompile: ~newAsyncId internal/async_hooks.js:254:20
      1  100.0%        LazyCompile: ~TickObject internal/process/next_tick.js:74:16
      1  100.0%          LazyCompile: ~nextTick internal/process/next_tick.js:96:20
      1  100.0%            LazyCompile: ~onwrite _stream_writable.js:445:17

      1    1.4%  v8::internal::compiler::CodeAssembler::ChangeInt32ToIntPtr(v8::internal::compiler::SloppyTNode<v8::internal::Word32T>)
      1  100.0%    v8::internal::Runtime_KeyedStoreIC_Miss(int, v8::internal::Object**, v8::internal::Isolate*)
      1  100.0%      LazyCompile: ~createUnsafeArrayBuffer buffer.js:115:33
      1  100.0%        LazyCompile: ~createPool buffer.js:124:20
      1  100.0%          Script: ~<anonymous> buffer.js:1:11
      1  100.0%            LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44

      1    1.4%  v8::internal::StackFrame::GetCallerState(v8::internal::StackFrame::State*) const
      1  100.0%    v8::internal::Runtime_LoadIC_Miss(int, v8::internal::Object**, v8::internal::Isolate*)
      1  100.0%      Script: ~<anonymous> v8.js:1:11
      1  100.0%        LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44
      1  100.0%          LazyCompile: ~NativeModule.require internal/bootstrap/loaders.js:137:34
      1  100.0%            Script: ~<anonymous> internal/error-serdes.js:1:11

      1    1.4%  v8::internal::Scanner::Scanner(v8::internal::UnicodeCache*)
      1  100.0%    v8::internal::Runtime_CompileLazy(int, v8::internal::Object**, v8::internal::Isolate*)
      1  100.0%      LazyCompile: ~realpathSync fs.js:1349:22
      1  100.0%        LazyCompile: ~toRealPath internal/modules/cjs/loader.js:201:20
      1  100.0%          LazyCompile: ~tryFile internal/modules/cjs/loader.js:193:17
      1  100.0%            LazyCompile: ~tryExtensions internal/modules/cjs/loader.js:208:23

      1    1.4%  v8::internal::HashTable<v8::internal::StringTable, v8::internal::StringTableShape>::Rehash()

      1    1.4%  v8::internal::HashTable<v8::internal::GlobalDictionary, v8::internal::GlobalDictionaryShape>::EnsureCapacity(v8::internal::Handle<v8::internal::GlobalDictionary>, int, v8::internal::PretenureFlag)
      1  100.0%    v8::internal::Builtin_ObjectDefineProperties(int, v8::internal::Object**, v8::internal::Isolate*)
      1  100.0%      LazyCompile: ~setupGlobalVariables internal/bootstrap/node.js:293:32
      1  100.0%        LazyCompile: ~startup internal/bootstrap/node.js:29:19
      1  100.0%          Script: ~bootstrapNodeJSCore internal/bootstrap/node.js:15:30

      1    1.4%  v8::internal::Factory::NewFeedbackVector(v8::internal::Handle<v8::internal::SharedFunctionInfo>, v8::internal::PretenureFlag)
      1  100.0%    v8::internal::Runtime_NewClosure(int, v8::internal::Object**, v8::internal::Isolate*)
      1  100.0%      Script: ~<anonymous> internal/buffer.js:1:1
      1  100.0%        node::contextify::ContextifyScript::RunInThisContext(v8::FunctionCallbackInfo<v8::Value> const&)
      1  100.0%          LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44
      1  100.0%            LazyCompile: ~NativeModule.require internal/bootstrap/loaders.js:137:34

      1    1.4%  v8::internal::Deserializer<v8::internal::DefaultDeserializerAllocator>::ReadData(v8::internal::MaybeObject**, v8::internal::MaybeObject**, int, unsigned char*)

      1    1.4%  v8::internal::DeclarationScope::AllocateScopeInfos(v8::internal::ParseInfo*, v8::internal::Isolate*, v8::internal::AnalyzeMode)
      1  100.0%    v8::internal::Runtime_CompileLazy(int, v8::internal::Object**, v8::internal::Isolate*)
      1  100.0%      LazyCompile: ~NativeModule.nonInternalExists internal/bootstrap/loaders.js:200:46
      1  100.0%        Builtin: ArrayFilter
      1  100.0%          Script: ~<anonymous> internal/modules/cjs/loader.js:1:11
      1  100.0%            LazyCompile: ~NativeModule.compile internal/bootstrap/loaders.js:230:44

      1    1.4%  v8::internal::CompilationCacheTable::PutRegExp(v8::internal::Handle<v8::internal::CompilationCacheTable>, v8::internal::Handle<v8::internal::String>, v8::base::Flags<v8::internal::JSRegExp::Flag, int>, v8::internal::Handle<v8::internal::FixedArray>)
      1  100.0%    v8::internal::Runtime_CreateRegExpLiteral(int, v8::internal::Object**, v8::internal::Isolate*)
      1  100.0%      LazyCompile: ~getColorDepth internal/tty.js:70:23
      1  100.0%        LazyCompile: ~Console.(anonymous function) console.js:175:49
      1  100.0%          LazyCompile: ~Console.(anonymous function) console.js:186:47
      1  100.0%            LazyCompile: ~log console.js:196:37

      1    1.4%  v8::internal::BufferedUtf16CharacterStream::ReadBlock()
      1  100.0%    v8::internal::Runtime_CompileLazy(int, v8::internal::Object**, v8::internal::Isolate*)
      1  100.0%      LazyCompile: ~Module._resolveLookupPaths internal/modules/cjs/loader.js:400:38
      1  100.0%        LazyCompile: ~Module._resolveFilename internal/modules/cjs/loader.js:547:35
      1  100.0%          LazyCompile: ~Module._load internal/modules/cjs/loader.js:502:24
      1  100.0%            LazyCompile: ~Module.require internal/modules/cjs/loader.js:629:36

      1    1.4%  v8::internal::AstRawString::AsArrayIndex(unsigned int*) const
      1  100.0%    v8::internal::Runtime_CompileLazy(int, v8::internal::Object**, v8::internal::Isolate*)
      1  100.0%      LazyCompile: ~Socket._writeGeneric net.js:734:42
      1  100.0%        LazyCompile: ~Socket._write net.js:771:35
      1  100.0%          LazyCompile: ~doWrite _stream_writable.js:400:17
      1  100.0%            LazyCompile: ~writeOrBuffer _stream_writable.js:360:23

      1    1.4%  std::ostream::sentry::sentry(std::ostream&)
      1  100.0%    v8::internal::Runtime_CompileLazy(int, v8::internal::Object**, v8::internal::Isolate*)
      1  100.0%      LazyCompile: ~startup internal/bootstrap/node.js:29:19
      1  100.0%        Script: ~bootstrapNodeJSCore internal/bootstrap/node.js:15:30

      1    1.4%  do_futex_wait.constprop.1

      1    1.4%  cfree

      1    1.4%  _dl_addr

      1    1.4%  _IO_file_sync
      1  100.0%    v8::internal::Runtime_DeserializeLazy(int, v8::internal::Object**, v8::internal::Isolate*)
      1  100.0%      LazyCompile: ~normalizeString path.js:57:25
      1  100.0%        LazyCompile: ~resolve path.js:1075:28
      1  100.0%          LazyCompile: ~Module._initPaths internal/modules/cjs/loader.js:748:29
      1  100.0%            Script: ~<anonymous> internal/modules/cjs/loader.js:1:11

      1    1.4%  LazyCompile: ~participants.sort /home/haus/Desktop/Bostrom/Genetics/Selection.js:82:22
      1  100.0%    LazyCompile: ~InsertionSort native array.js:500:23
      1  100.0%      LazyCompile: ~QuickSort native array.js:531:19
      1  100.0%        LazyCompile: ~InnerArraySort native array.js:487:24
      1  100.0%          LazyCompile: ~sort native array.js:708:46
      1  100.0%            LazyCompile: module.exports.Tournament /home/haus/Desktop/Bostrom/Genetics/Selection.js:67:38

      1    1.4%  LazyCompile: module.exports.Tournament /home/haus/Desktop/Bostrom/Genetics/Selection.js:67:38
      1  100.0%    Script: ~<anonymous> /home/haus/Desktop/Bostrom/test.js:1:11
      1  100.0%      LazyCompile: ~Module._compile internal/modules/cjs/loader.js:650:37
      1  100.0%        LazyCompile: ~Module._extensions..js internal/modules/cjs/loader.js:698:37
      1  100.0%          LazyCompile: ~Module.load internal/modules/cjs/loader.js:590:33
      1  100.0%            LazyCompile: ~tryModuleLoad internal/modules/cjs/loader.js:535:23

      1    1.4%  /lib/x86_64-linux-gnu/libc-2.27.so

Solution

  • V8 developer here.

    What you're seeing is mostly the effect of functions getting optimized one after another (as opposed to iteration count itself influencing time per iteration). There isn't really anything you can do about that; that's how modern JavaScript engines work. Is it actually causing problems?

    One way to show that performance is quite consistent after warmup is when you reverse the order of your warmup cases. (Cue the usual reminder about misleading microbenchmarks ;-) )

    The profiling result in your edit is weird; I'm seeing something different. Maybe you got the wrong isolate? As you assumed, sorting is the most expensive part, at about 60% of time spent. And as you correctly observed, the smaller you make tournamentSize, the faster each iteration. Array.prototype.sort is a (highly optimized) internal built-in, so its performance is indeed beyond your control. Passing in a custom comparator function has a significant performance cost, but in this case I don't see a way around it.