File Coverage

blib/lib/Benchmark/Perl/Formance/Plugin/Mandelbrot/withthreads.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Benchmark::Perl::Formance::Plugin::Mandelbrot::withthreads;
2             our $AUTHORITY = 'cpan:SCHWIGON';
3             # ABSTRACT: benchmark - Generate Mandelbrot set portable bitmap file - using threads
4             $Benchmark::Perl::Formance::Plugin::Mandelbrot::withthreads::VERSION = '0.001';
5             # COMMAND LINE:
6             # /usr/bin/perl mandelbrot.perl 16000
7              
8             # The Computer Language Benchmarks Game
9             # http://shootout.alioth.debian.org/
10             # implemented by Greg Buchholz
11             # streamlined by Kalev Soikonen
12             # parallelised by Philip Boulain
13             # modified by Jerry D. Hedden
14             # Benchmark::Perl::Formance plugin by Steffen Schwigon
15             # - nr of threads now dynamically
16              
17 1     1   8 use strict;
  1         2  
  1         64  
18 1     1   7 use warnings;
  1         2  
  1         66  
19 1     1   927 use threads;
  0            
  0            
20              
21             #############################################################
22             # #
23             # Benchmark Code ahead - Don't touch without strong reason! #
24             # #
25             #############################################################
26              
27             use Benchmark ':hireswallclock';
28              
29             use constant ITER => 50;
30             use constant LIMITSQR => 2.0 ** 2;
31             use constant MAXPIXEL => 524288; # Maximum pixel buffer per thread
32              
33             my ($w, $h);
34             my $threads;
35              
36             # Generate pixel data for a single dot
37             sub dot($$) { ## no critic
38             my ($Zr, $Zi, $Tr, $Ti) = (0.0,0.0,0.0,0.0);
39             my $i = ITER;
40             my $Cr = 2 * $_[0] / $w - 1.5;
41             my $Ci = 2 * $_[1] / $h - 1.0;
42             (
43             $Zi = 2.0 * $Zr * $Zi + $Ci,
44             $Zr = $Tr - $Ti + $Cr,
45             $Ti = $Zi * $Zi,
46             $Tr = $Zr * $Zr
47             ) until ($Tr + $Ti > LIMITSQR || !$i--);
48             return ($i == -1);
49             }
50              
51             # Generate pixel data for range of lines, inclusive
52             sub lines($$) { ## no critic
53             map { my $y = $_;
54             pack 'B*', pack 'C*', map dot($_, $y), 0..$w-1;
55             } $_[0]..$_[1]
56             }
57              
58             sub num_cpus {
59             open my $fh, '<', '/proc/cpuinfo' or return;
60             my $cpus;
61             while (<$fh>) {
62             $cpus ++ if /^processor[\s]+:/; # 0][]0]; # for emacs cperl-mode indent bug
63             }
64             return $cpus;
65             }
66              
67             sub run
68             {
69             $w = $h = shift;
70             $threads = num_cpus() + 1; # Workers; ideally slightly overshoots number of processors
71              
72             # Decide upon roughly equal batching of workload, within buffer limits
73             $threads = $h if $threads > $h;
74             my $each = int($h / $threads);
75             $each = int(MAXPIXEL / $w) if ($each * $w) > MAXPIXEL;
76             $each = 1 if $each < 1;
77              
78             # Work as long as we have lines to spawn for or threads to collect from
79             $| = 1;
80             #print "P4\n$w $h\n";
81             my $y = 0;
82             my @workers;
83             while (@workers or ($y < $h)) {
84             # Create workers up to requirement
85             while ((@workers < $threads) and ($y < $h)) {
86             my $y2 = $y + $each;
87             $y2 = $h if $y2 > $h;
88             push(@workers, threads->create('lines', $y, $y2 - 1));
89             $y = $y2;
90             }
91             # Block for result from the leading thread (to keep output in order)
92             my $next = shift @workers;
93             #print
94             $next->join();
95             }
96             }
97              
98             sub main
99             {
100             my ($options) = @_;
101              
102             my $goal = $options->{fastmode} ? 400 : 2_000;
103             my $count = $options->{fastmode} ? 1 : 5;
104              
105             my $result;
106             my $t = timeit $count, sub { $result = run($goal) };
107             return {
108             Benchmark => $t,
109             goal => $goal,
110             count => $count,
111             # result => $result, # useless here
112             threads => $threads,
113             };
114             }
115              
116             1;
117              
118             __END__