use strict;
use Benchmark 'cmpthese';
use Bit::Vector;
# Länge % 8 == 0 trifft nicht immer zu, hier aber der Einfachkeit halber ang.
my $length = 1_000_000;
# Das Bitmuster hat direkt Einfluss darauf, wie oft ein neuer Wert an @cache
# angehängt und wie oft $cache[-1] erhöht bzw. verringert wird.
# Wir kennen es nicht, da es durch den Nutzer (mittelbar) festgelegt wird.
# Die von rand erzeugte 0<>1-Wechselfrequenz ist eigentlich viel zu groß,
# aber wollen wir uns mal nicht verkünsteln ...
my @array = map { split '', unpack 'B*', pack('C', int rand(256)) }
1 .. $length/8;
# Vorschlag von topeg++. Der Umweg über $bin_data ist mE unnötig.
# Zwar bräuchte das ähnlich wenig Speicher wie ein Bitvektor, aber da er bei
# jedem Funktionsaufruf neu aufgeblasen werden muss, nützt uns das nicht viel.
# So sparen wir uns wenigstens die Verwaltungsinformationen des Arrays und
# von 999.999 Skalaren
my $str_data = join '', @array;
my $vec = Bit::Vector->new_Bin(1_000_000, $str_data);
# Vorschlag von raubtier++: GeXOR'ter Kovektor
my $vex = $vec->Clone;
$vex->shift_left(!$vec->lsb);
$vex->ExclusiveOr($vex,$vec);
# Perl optimiert nicht für Methodenaufrufe (s. Benchmark unten)
# Daher müssen wir das machen:
*test = $vec->can('bit_test');
my ($start,$end) = (0,999_999);
cmpthese(100, {
# über Array-Slice iterieren:
slice => sub {
my (@cache, $last);
for (@array[$start..$end]) {
if ($_ xor $last // !$_) { push @cache, $_ ? 1 : -1 }
else { $cache[-1] += $_ }
$last = $_;
}
},
index => sub {
my (@cache,$last);
for ( my $i = $start; $i <= $end; $i++ ) {
my $v = $array[$i];
if ($v xor $last // !$v) { push @cache, $v ? 1 : -1 }
else { $cache[-1] += $v }
$last = $v;
}
},
# vorher in ein eigenes Array kopieren, denn darüber wird schneller iteriert:
array => sub {
my @array = @array[$start .. $end];
my (@cache,$last);
for ( @array ) {
if ($_ xor $last // !$_) { push @cache, $_ ? 1 : -1 }
else { $cache[-1] += $_ }
$last = $_;
}
},
# Müssten wir stets über das ganze @array laufen, gäbs also weder @start noch @end:
theor => sub {
# my @array = @array[$start .. $end];
my (@cache, $last);
for (@array) {
if ($_ xor $last // !$_) { push @cache, $_ ? 1 : -1 }
else { $cache[-1] += $_ }
$last = $_;
}
},
# Subroutinenaufruf an Bit::Vector, d.h. gecachter Methodenaufruf:
bitvr => sub {
my (@cache, $last);
for ( my $i = $start; $i <= $end; $i++ ) {
my $v = test($vec,$i);
if ($v xor $last // !$v) { push @cache, $v ? 1 : -1 }
else { $cache[-1] += $v }
$last = $v;
}
},
# ungecacht:
bitvm => sub {
my (@cache, $last);
for ( my $i = $start; $i <= $end; $i++ ) {
my $v = $vec->bit_test($i);
if ($v xor $last // !$v) { push @cache, $v ? 1 : -1 }
else { $cache[-1] += $v }
$last = $v;
}
},
# Test gegen XOR'd Covektor, Vorschlag von raubtier++ ($vex-Init. oben):
bitvx => sub {
my (@cache, $last);
for ( my $i = $start; $i <= $end; $i++ ) {
my $v = test($vec,$i);
if (test($vex,$i)) { push @cache, $v ? 1 : -1 }
else { $cache[-1] += $v }
# $last = $v; -- EDIT
}
},
# Sparen wir uns die Strukturdaten des Arrays und von 999.999 Skalaren
# Vorschlag von topeg++, angeglichen. $bit_data-Initialisierung oben.
strng => sub {
my (@cache, $last);
for ( my $i = $start; $i <= $end; $i++ ) {
my $v = substr($str_data,$_,1);
if (test($vex,$i)) { push @cache, $v ? 1 : -1 }
else { $cache[-1] += $v }
$last = $v;
}
},
}
);