#!/usr/bin/perl # Can be invoked as: # ./bench.PL # (or 'make bench') generates FU/Benchmarks.pod # ./bench.PL regex # run benchmark(s) matching the regex use v5.36; use builtin 'true', 'false'; use Benchmark ':hireswallclock', 'timethis'; use Config; my $modules = join '', map sprintf("=item L<%s> %s\n\n", $_, eval "require $_; \$${_}::VERSION"), qw/ FU Cpanel::JSON::XS JSON::PP JSON::XS JSON::SIMD /; my(%bench, @bench); sub bench($name, @arg) { push @bench, $name; $bench{$name} = \@arg; } sub runbench($text, @f) { print "$text\n\n"; # TODO: Should include variance; factor-compared-to-slowest might be cool too for my ($t, $f) (@f) { my $o = timethis -1, $f, 0, 'none'; printf " %18s%10d/s\n", $t, $o->iters/$o->real; } print "\n"; } sub runbenches($re) { runbench $bench{$_}->@* for grep /$re/, @bench; } # Use similar options for fair comparisons. my $j_cp = Cpanel::JSON::XS->new->allow_nonref->unblessed_bool->convert_blessed; my $j_pp = JSON::PP->new->allow_nonref->core_bools->convert_blessed; my $j_xs = JSON::XS->new->allow_nonref->boolean_values([false,true])->convert_blessed; my $j_si = JSON::SIMD->new->allow_nonref->core_bools->convert_blessed; use FU::Util 'json_format'; sub jsonfmt($name, $text, $data) { bench "jsonfmt/$name", $text, 'JSON::PP', sub { $j_pp->encode($data) }, 'Cpanel::JSON::XS',sub { $j_cp->encode($data) }, 'JSON::SIMD', sub { $j_si->encode($data) }, 'JSON::XS', sub { $j_xs->encode($data) }, 'FU::Util', sub { json_format $data }; } # From JSON::XS POD. jsonfmt api => 'API object from L documentation.', [ map +{method => 'handleMessage', params => ['user1','we were just talking'], 'id' => undef, 'array' => [1,11,234,-5,1e5,1e7,1,0]}, 1..10 ]; jsonfmt ints => 'Small integers', [ -5000..5000 ]; jsonfmt intl => 'Large integers', [ map { my $n=$_; map +($n+1<<$_), 10..60 } 1..10 ]; jsonfmt strs => 'ASCII strings', [ map +('hello, world', 'one more string', 'another string'), 1..100 ]; jsonfmt stru => 'Unicode strings', do { use utf8; [ map +('グリザイアの果実 -LE FRUIT DE LA GRISAIA-', '💩', 'Я люблю нічого не робити'), 1..50 ]; }; jsonfmt stres => 'String escaping (few)', [ map 'This string needs to "be escaped" a little bit', 1..100 ]; jsonfmt strel => 'String escaping (many)', [ map "This \" \\ needs \b\x01\x02\x03\x04 more", 1..100 ]; if (!@ARGV || $ARGV[0] eq 'bench') { chomp(my $date = `date +%F`); print "Writing to FU/Benchmarks.pod...\n"; open my $F, '>FU/Benchmarks.pod' or die $!; select $F; while () { s/^:modules/$modules/; s/^:benches (.+)/runbenches $1/e; s/^:context/These benchmarks were performed on $date with perl $^V on $Config{archname}./; print; } } else { runbenches $_ for @ARGV; } __DATA__ =head1 NAME FU::Benchmarks - A bunch of automated benchmark results. =head1 DESCRIPTION This file is automatically generated from 'bench.pl' in the L distribution. These benchmarks compare performance of some FU functionality against similar modules found on CPAN. =head1 CONTEXT :context The following module versions were used: =over :modules =back =head1 BENCHMARKS =head2 JSON Formatting These benchmarks run on large-ish arrays with repeated values. JSON encoding is sufficiently fast that Perl function calling overhead tends to dominate for smaller inputs, but I don't find that overhead very interesting. Other modules will likely do better in benchmarks on small inputs. Also worth noting that JSON::SIMD formatting code is forked from JSON::XS, the SIMD parts are only used for parsing. :benches ^jsonfmt