Les boucles ne sont pas équivalentes, et vous écrasez principalement le paquetage bigint et cela n'a rien à voir avec for
par rapport à while
en soi.
La boucle while utilise la notation '$s *= $i
' mais la boucle for utilise '$s = $s * $i
'. Il est assez simple de démontrer que ceux-ci ne sont pas identiques. En outre, une boucle compte; l'autre compte à rebours. Cela affecte la taille des nombres à multiplier. C'est un effet de second ordre - mais pas complètement négligeable.
[Mise à jour: mise à jour pour afficher une seule version du code, avec des sous-secondes. Il y a de la place pour penser que l'impression devrait être exclue des calculs de synchronisation; Cela rend les choses plus désordonnées, donc je n'ai pas dérangé. J'ai corrigé le bug dans la version précédente: la boucle 4 était la même que la boucle 3 - maintenant ce n'est pas le cas. J'ai également mis au point la mise en forme de la sortie (bien que la gestion des sous-secondes puisse être améliorée - un exercice pour le lecteur), et il existe de meilleurs rapports sur les progrès.]
Les résultats de synchronisation sur un Mac Mini (Snow Leopard 10.6.2) étaient les suivants:
Count up $s *= $i: 00:00:12.663337
Count up $s = $s * $i: 00:00:20.686111
Count down $s *= $i: 00:00:14.201797
Count down $s = $s * $i: 00:00:23.269874
Le script:
use Time::HiRes qw(gettimeofday);
use strict;
use warnings;
use bigint;
use constant factorial_of => 13000;
sub delta_t
{
my($tag, $t1, $t2) = @_;
my($d) = int($t2 - $t1);
my($f) = ($t2 - $t1) - $d;
my($s) = sprintf("%.6f", $f);
$s =~ s/^0//;
printf "%-25s %02d:%02d:%02d%s\n",
$tag, int($d/3600), int(($d % 3600)/60), int($d % 60), $s;
}
my $t1 = gettimeofday;
{
my $n = factorial_of;
my $s = 1;
for (my $i = 2; $i <= $n; $i++)
{
$s *= $i;
}
print "$s\n: Loop 1\n";
}
my $t2 = gettimeofday;
delta_t('Count up $s *= $i:', $t1, $t2);
{
my $n = factorial_of;
my $s = 1;
for (my $i = 2; $i <= $n; $i++)
{
$s = $s * $i;
}
print "$s\n: Loop 2\n";
}
my $t3 = gettimeofday;
delta_t('Count up $s *= $i:', $t1, $t2);
delta_t('Count up $s = $s * $i:', $t2, $t3);
{
my $n = factorial_of;
my $s = 1;
for (my $i = $n; $i > 1; $i--)
{
$s *= $i;
}
print "$s\n: Loop 3\n";
}
my $t4 = gettimeofday;
delta_t('Count up $s *= $i:', $t1, $t2);
delta_t('Count up $s = $s * $i:', $t2, $t3);
delta_t('Count down $s *= $i:', $t3, $t4);
{
my $n = factorial_of;
my $s = 1;
for (my $i = $n; $i > 1; $i--)
{
$s = $s * $i;
}
print "$s\n: Loop 4\n";
}
my $t5 = gettimeofday;
delta_t('Count up $s *= $i:', $t1, $t2);
delta_t('Count up $s = $s * $i:', $t2, $t3);
delta_t('Count down $s *= $i:', $t3, $t4);
delta_t('Count down $s = $s * $i:', $t4, $t5);
Et voici une version beaucoup plus compacte le code ci-dessus, étendu pour tester 'while' boucles ainsi que 'for' boucles. Il traite également de la plupart des problèmes de synchronisation. La seule chose qui n'est pas idéale (pour moi) est qu'elle utilise un couple de variables globales, et j'ai légèrement modifié le code dans le code refs pour que tout se place sur une ligne sans déclencher de barre de défilement (sur mon écran, de toute façon). Clairement, avec un peu plus de travail, le test pourrait être enveloppé dans un tableau, de sorte que le test serait fait de manière itérative - une boucle dans le tableau exécutant la fonction timer sur les informations dans le tableau. Etc...c'est un SMOP - simple question de programmation. (Il imprime le hachage MD5 de la factorielle, plutôt que la factorielle elle-même, car il est plus facile de comparer les résultats, etc ... Il a fait ressortir quelques erreurs alors que je refaisais le code ci-dessus: Oui, MD5 n'est pas sécurisé - mais je ne l'utilise pas pour la sécurité, il suffit de repérer les changements involontaires)
use Time::HiRes qw(gettimeofday);
use Digest::MD5 qw(md5_hex);
use strict;
use warnings;
use bigint;
use constant factorial_of => 13000;
my ($s, $i);
my $l1 = sub {my($n) = @_; for ($i = 2; $i <= $n; $i++) { $s *= $i; }};
my $l2 = sub {my($n) = @_; for ($i = 2; $i <= $n; $i++) { $s = $s * $i; }};
my $l3 = sub {my($n) = @_; for ($i = $n; $i > 1; $i--) { $s *= $i; }};
my $l4 = sub {my($n) = @_; for ($i = $n; $i > 1; $i--) { $s = $s * $i; }};
my $l5 = sub {my($n) = @_; $i = 2; while ($i <= $n) { $s *= $i; $i++; }};
my $l6 = sub {my($n) = @_; $i = 2; while ($i <= $n) { $s = $s * $i; $i++; }};
my $l7 = sub {my($n) = @_; $i = $n; while ($i > 1) { $s *= $i; $i--; }};
my $l8 = sub {my($n) = @_; $i = $n; while ($i > 1) { $s = $s * $i; $i--; }};
sub timer
{
my($n, $code, $tag) = @_;
my $t1 = gettimeofday;
$s = 1;
&$code(factorial_of);
my $t2 = gettimeofday;
my $md5 = md5_hex($s);
printf "Loop %d: %-33s %09.6f (%s)\n", $n, $tag, $t2 - $t1, $md5;
}
my $count = 1;
timer($count++, $l1, 'for - Count up $s *= $i:');
timer($count++, $l2, 'for - Count up $s = $s * $i:');
timer($count++, $l3, 'for - Count down $s *= $i:');
timer($count++, $l4, 'for - Count down $s = $s * $i:');
timer($count++, $l5, 'while - Count up $s *= $i:');
timer($count++, $l6, 'while - Count up $s = $s * $i:');
timer($count++, $l7, 'while - Count down $s *= $i:');
timer($count++, $l8, 'while - Count down $s = $s * $i:');
Exemple de sortie (checksum MD5 comprimé pour éviter la rupture de la ligne - la pleine valeur est 584b3ab832577fd1390970043efc0ec8
):
Loop 1: for - Count up $s *= $i: 12.853630 (584b3ab8...3efc0ec8)
Loop 2: for - Count up $s = $s * $i: 20.854735 (584b3ab8...3efc0ec8)
Loop 3: for - Count down $s *= $i: 14.798155 (584b3ab8...3efc0ec8)
Loop 4: for - Count down $s = $s * $i: 23.699913 (584b3ab8...3efc0ec8)
Loop 5: while - Count up $s *= $i: 12.972428 (584b3ab8...3efc0ec8)
Loop 6: while - Count up $s = $s * $i: 21.192956 (584b3ab8...3efc0ec8)
Loop 7: while - Count down $s *= $i: 14.555620 (584b3ab8...3efc0ec8)
Loop 8: while - Count down $s = $s * $i: 23.790795 (584b3ab8...3efc0ec8)
I. régulièrement voir une petite pénalité (< 1%) pour la boucle 'while' sur la boucle 'for' correspondante, mais je n'ai pas de bonne explication pour il.
Vous essayez probablement d'optimiser la mauvaise chose. Je me demande pourquoi vous pensez que cette partie particulière de la langue est si importante? – Ether
Exécutez-les à plusieurs reprises, et ils auront probablement la moyenne à peu près au même – CaffGeek
@Chad, en fait j'ai déjà testé le code à plusieurs reprises. Ils ont pris différents temps pour terminer le même travail. Je pense que l'explication de Jonathan Leffler avec le code d'illustration est très logique. – Mike