@@ -225,16 +225,27 @@ struct progress_bar {
225225 }
226226};
227227
228+ /* *
229+ * The speed at which we can allocate memory is strictly system specific.
230+ * It depends on the OS and the runtime library. It is subject to various
231+ * system-specific knobs. It is not something that we can reasonably
232+ * benchmark with crude timings.
233+ * If someone wants to optimize how simdjson allocate memory, then it will
234+ * almost surely require a distinct benchmarking tool. What is meant by
235+ * "memory allocation" also requires a definition. Doing "new char[size]" can
236+ * do many different things depending on the system.
237+ */
238+
228239enum class BenchmarkStage {
229- ALL ,
240+ ALL , // This excludes allocation
230241 ALLOCATE ,
231242 STAGE1 ,
232243 STAGE2
233244};
234245
235246const char * benchmark_stage_name (BenchmarkStage stage) {
236247 switch (stage) {
237- case BenchmarkStage::ALL : return " All" ;
248+ case BenchmarkStage::ALL : return " All (Without Allocation) " ;
238249 case BenchmarkStage::ALLOCATE : return " Allocate" ;
239250 case BenchmarkStage::STAGE1 : return " Stage 1" ;
240251 case BenchmarkStage::STAGE2 : return " Stage 2" ;
@@ -253,8 +264,8 @@ struct benchmarker {
253264 // Statistics about the JSON file independent of its speed (amount of utf-8, structurals, etc.).
254265 // Loaded on first parse.
255266 json_stats* stats;
256- // Speed and event summary for full parse (including allocation, stage 1 and stage 2)
257- event_aggregate all_stages {};
267+ // Speed and event summary for full parse (stage 1 and stage 2, but *excluding* allocation )
268+ event_aggregate all_stages_without_allocation {};
258269 // Speed and event summary for stage 1
259270 event_aggregate stage1{};
260271 // Speed and event summary for stage 2
@@ -285,23 +296,24 @@ struct benchmarker {
285296
286297 const event_aggregate& operator [](BenchmarkStage stage) const {
287298 switch (stage) {
288- case BenchmarkStage::ALL : return this ->all_stages ;
299+ case BenchmarkStage::ALL : return this ->all_stages_without_allocation ;
289300 case BenchmarkStage::STAGE1 : return this ->stage1 ;
290301 case BenchmarkStage::STAGE2 : return this ->stage2 ;
291302 case BenchmarkStage::ALLOCATE : return this ->allocate_stage ;
292- default : exit_error (" Unknown stage" ); return this ->all_stages ;
303+ default : exit_error (" Unknown stage" ); return this ->all_stages_without_allocation ;
293304 }
294305 }
295306
296307 int iterations () const {
297- return all_stages .iterations ;
308+ return all_stages_without_allocation .iterations ;
298309 }
299310
300311 really_inline void run_iteration (bool stage1_only, bool hotbuffers=false ) {
301312 // Allocate dom::parser
302313 collector.start ();
303314 dom::parser parser;
304- error_code error = parser.allocate (json.size ());
315+ // We always allocate at least 64KB. Smaller allocations may actually be slower under some systems.
316+ error_code error = parser.allocate (json.size () < 65536 ? 65536 : json.size ());
305317 if (error) {
306318 exit_error (string (" Unable to allocate_stage " ) + to_string (json.size ()) + " bytes for the JSON result: " + error_message (error));
307319 }
@@ -329,7 +341,7 @@ struct benchmarker {
329341 // Stage 2 (unified machine) and the rest
330342
331343 if (stage1_only) {
332- all_stages << stage1_count;
344+ all_stages_without_allocation << stage1_count;
333345 } else {
334346 event_count stage2_count;
335347 collector.start ();
@@ -339,7 +351,7 @@ struct benchmarker {
339351 }
340352 stage2_count = collector.end ();
341353 stage2 << stage2_count;
342- all_stages << allocate_count + stage1_count + stage2_count;
354+ all_stages_without_allocation << stage1_count + stage2_count;
343355 }
344356 // Calculate stats the first time we parse
345357 if (stats == NULL ) {
@@ -386,7 +398,7 @@ struct benchmarker {
386398 prefix,
387399 " Speed" ,
388400 stage.elapsed_ns () / static_cast <double >(stats->blocks ), // per block
389- percent (stage.elapsed_sec (), all_stages .elapsed_sec ()), // %
401+ percent (stage.elapsed_sec (), all_stages_without_allocation .elapsed_sec ()), // %
390402 stage.elapsed_ns () / static_cast <double >(stats->bytes ), // per byte
391403 stage.elapsed_ns () / static_cast <double >(stats->structurals ), // per structural
392404 (static_cast <double >(json.size ()) / 1000000000.0 ) / stage.elapsed_sec () // GB/s
@@ -397,7 +409,7 @@ struct benchmarker {
397409 prefix,
398410 " Cycles" ,
399411 stage.cycles () / static_cast <double >(stats->blocks ),
400- percent (stage.cycles (), all_stages .cycles ()),
412+ percent (stage.cycles (), all_stages_without_allocation .cycles ()),
401413 stage.cycles () / static_cast <double >(stats->bytes ),
402414 stage.cycles () / static_cast <double >(stats->structurals ),
403415 (stage.cycles () / stage.elapsed_sec ()) / 1000000000.0
@@ -406,7 +418,7 @@ struct benchmarker {
406418 prefix,
407419 " Instructions" ,
408420 stage.instructions () / static_cast <double >(stats->blocks ),
409- percent (stage.instructions (), all_stages .instructions ()),
421+ percent (stage.instructions (), all_stages_without_allocation .instructions ()),
410422 stage.instructions () / static_cast <double >(stats->bytes ),
411423 stage.instructions () / static_cast <double >(stats->structurals ),
412424 stage.instructions () / static_cast <double >(stage.cycles ())
@@ -417,9 +429,9 @@ struct benchmarker {
417429 prefix,
418430 " Misses" ,
419431 stage.branch_misses (),
420- percent (stage.branch_misses (), all_stages .branch_misses ()),
432+ percent (stage.branch_misses (), all_stages_without_allocation .branch_misses ()),
421433 stage.cache_misses (),
422- percent (stage.cache_misses (), all_stages .cache_misses ()),
434+ percent (stage.cache_misses (), all_stages_without_allocation .cache_misses ()),
423435 stage.cache_references ()
424436 );
425437 }
@@ -456,14 +468,14 @@ struct benchmarker {
456468 allocate_stage.best .cycles () / static_cast <double >(json.size ()),
457469 stage1.best .cycles () / static_cast <double >(json.size ()),
458470 stage2.best .cycles () / static_cast <double >(json.size ()),
459- all_stages .best .cycles () / static_cast <double >(json.size ()),
460- gb / all_stages .best .elapsed_sec (),
471+ all_stages_without_allocation .best .cycles () / static_cast <double >(json.size ()),
472+ gb / all_stages_without_allocation .best .elapsed_sec (),
461473 gb / stage1.best .elapsed_sec (),
462474 gb / stage2.best .elapsed_sec ());
463475 } else {
464476 printf (" \" %s\"\t\t\t\t\t %f\t %f\t %f\n " ,
465477 base,
466- gb / all_stages .best .elapsed_sec (),
478+ gb / all_stages_without_allocation .best .elapsed_sec (),
467479 gb / stage1.best .elapsed_sec (),
468480 gb / stage2.best .elapsed_sec ());
469481 }
@@ -490,10 +502,10 @@ struct benchmarker {
490502 stats->blocks_with_16_structurals_flipped , percent (stats->blocks_with_16_structurals_flipped , stats->blocks ));
491503 }
492504 printf (" \n " );
493- printf (" All Stages\n " );
494- print_aggregate (" | " , all_stages .best );
505+ printf (" All Stages (excluding allocation) \n " );
506+ print_aggregate (" | " , all_stages_without_allocation .best );
495507 // frequently, allocation is a tiny fraction of the running time so we omit it
496- if (allocate_stage.best .elapsed_sec () > 0.01 * all_stages .best .elapsed_sec ()) {
508+ if (allocate_stage.best .elapsed_sec () > 0.01 * all_stages_without_allocation .best .elapsed_sec ()) {
497509 printf (" |- Allocation\n " );
498510 print_aggregate (" | " , allocate_stage.best );
499511 }
@@ -504,17 +516,16 @@ struct benchmarker {
504516 if (collector.has_events ()) {
505517 double freq1 = (stage1.best .cycles () / stage1.best .elapsed_sec ()) / 1000000000.0 ;
506518 double freq2 = (stage2.best .cycles () / stage2.best .elapsed_sec ()) / 1000000000.0 ;
507- double freqall = (all_stages .best .cycles () / all_stages .best .elapsed_sec ()) / 1000000000.0 ;
519+ double freqall = (all_stages_without_allocation .best .cycles () / all_stages_without_allocation .best .elapsed_sec ()) / 1000000000.0 ;
508520 double freqmin = min (freq1, freq2);
509521 double freqmax = max (freq1, freq2);
510522 if ((freqall < 0.95 * freqmin) or (freqall > 1.05 * freqmax)) {
511523 printf (" \n Warning: The processor frequency fluctuates in an expected way!!!\n "
512- " Expect the overall speed not to match stage 1 and stage 2 speeds.\n "
513524 " Range for stage 1 and stage 2 : [%.3f GHz, %.3f GHz], overall: %.3f GHz.\n " ,
514525 freqmin, freqmax, freqall);
515526 }
516527 }
517- printf (" \n %.1f documents parsed per second (best)\n " , 1.0 /static_cast <double >(all_stages .best .elapsed_sec ()));
528+ printf (" \n %.1f documents parsed per second (best)\n " , 1.0 /static_cast <double >(all_stages_without_allocation .best .elapsed_sec ()));
518529 }
519530 }
520531};
0 commit comments