Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

While I'm waiting for my password ....

My computer is a new Dell Inspiron with an Intel i9 3900.

I wrote a cli similation of a simple solitaire card game that I knew the winning stats for.

I was disappointed in the results; but here is the culprit.

pops and shifts and individual selections from lists were unstable about 5% of the time.

Lots of run-time warning msgs about uninitiated values and more failures that went undetected.

Unitiated cards in the source list, and uninitiated variables supposed to have receive popped or selected cards

The code sequence causing the problem was : search a nine card list for a pair, cover both cards of a pair with cards from the stock, search the updated list for a pair or pairs.

One or both of the covering cards never arrived maybe 5% of the time.

So, the basic question is : with twenty some CPU cores looking for work, is Perl really up for this environment?

And obviously, any other posts on this?

Replies are listed 'Best First'.
Re: pop() on a fast multi-core cpu
by jdporter (Paladin) on Oct 31, 2024 at 02:13 UTC

    I always (well, sometimes) enjoy trying my hand at rewriting other people's crap code.

    #!/usr/bin/perl use Time::HiRes qw(gettimeofday tv_interval); use List::Util 'shuffle'; # perlfaq4 use warnings; use strict; my @ups = ('0') x 10; # four suits are added to a deck and then shuffled my @deck = shuffle(('A' .. 'M') x 4); #print "@deck\n"; exit 0; print "\n"; my $t0 = gettimeofday; $ups[0] = shift @deck; eval { my $done = 0; while (!$done) { print "@ups\n"; $done = 1; scan: for (my $j=1;$j<10;$j++) { $ups[$j] eq '0' and $ups[$j] = (shift @deck or die); for (my $k=0;$k<$j;$k++) { if ( $ups[$j] eq $ups[$k] ) { $ups[$j] = (shift @deck or die); $ups[$k] = (shift @deck or die); $done = 0; last scan; } } } } }; print "\n@ups\tExit row\n"; my $usec = sprintf("%.2f", (gettimeofday - $t0) * 10**6); print "\n".(52-@deck)." passes in $usec microseconds. "; print !@deck ? "Succeeded in consuming the entire deck.\n\n" : "Failed to consume the entire deck.\n\n";

    Of course, if you have input/output, such as those print statements, there's virtually no meaning to any timings you get.

    One thing I don't like about the OP's solution (which I didn't change in mine) is that there is a lot of unnecessary checking on every pass. For example, if on pass N we cover spots 5 and 6, then there's no need to check on pass N+1 whether spots 3 and 4 are a pair.

      TIMTOWTDI - or perhaps I misunderstand the problem...

      #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11162510 use warnings; use Time::HiRes qw( time ); use List::AllUtils qw( shuffle ); my @stock = shuffle split //, 'A23456789TJQK' x 4; my $ninecardlist = join '', splice @stock, 0, 9; my $start = time; while( @stock > 1 ) { print "$ninecardlist\n"; my ($left, $right) = splice @stock, 0, 2; $ninecardlist =~ s/(.)(.*?)\1/ print "play $left & $right over $1's\n"; "$left$2$right"/e or last; } @stock > 1 or print "$ninecardlist\n"; print @stock < 2 ? "Succeeded in consuming the entire deck.\n" : "Failed to consume the entire deck.\n"; printf "%d passes in %d microseconds\n", 52 - @stock, (time - $start) +* 1e6;

      Outputs:

      T7AJJQ342 play A & 9 over J's T7AA9Q342 play 8 & T over A's T78T9Q342 play T & 3 over T's T7839Q342 play 8 & 5 over 3's T7889Q542 play 5 & 6 over 8's T7569Q542 play 6 & 7 over 5's T7669Q742 play 9 & 8 over 7's T9669Q842 play A & 6 over 9's TA666Q842 play 4 & K over 6's TA4K6Q842 play 3 & A over 4's TA3K6Q8A2 play Q & J over A's TQ3K6Q8J2 play 7 & 6 over Q's T73K668J2 play T & 4 over 6's T73KT48J2 play K & Q over T's K73KQ48J2 play 7 & 3 over K's 7733Q48J2 play 2 & 4 over 7's 2433Q48J2 play 8 & 9 over 2's 8433Q48J9 play J & 2 over 8's J433Q42J9 play 9 & K over J's 9433Q42K9 play 2 & K over 9's 2433Q42KK play Q & 5 over 2's Q433Q45KK Succeeded in consuming the entire deck. 51 passes in 125 microseconds

        Regexes! I love it!
        It just goes to show:
        TIAABWTDI (There is always a better way to do it) :-)

        Note the OP used a ten card list (or am I missing something?)
Re: pop() on a fast multi-core cpu
by LanX (Saint) on Oct 30, 2024 at 19:27 UTC
    I'd say the problem is most likely in front of the keyboard.

    Of course you could prove us wrong by supplying an SSCCE to reproduce the "issue" instead of prose blaming Perl for warnings about obviously broken code...

    But hey, we all know that baseless ranting is more entertaining, right?

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    see Wikisyntax for the Monastery

Re: pop() on a fast multi-core cpu
by Anonymous Monk on Oct 30, 2024 at 19:23 UTC

    Additionally, all code is sequential, NO events!

    Very simple structure allows all redirections to be goto Top of code for next pass.

      Unless you show the code, your post is meaningless at best, more likely just a troll.

      I dont get an warnings or errors running your code - but I have not tried to figure out its logic because, with respect, it written like garbage.

      What version of perl are you running?

Re: pop() on a fast multi-core cpu
by Anonymous Monk on Oct 30, 2024 at 22:18 UTC
    #!/usr/bin/perl use warnings; use strict; use Time::HiRes qw(gettimeofday tv_interval) +; my( @suit, @deck); my @ups = qw( 0 0 0 0 0 0 0 0 0 0); my $cntr = 0; Deck(); # four shuffled suits are added to a deck and then shuffled print "\n"; my $t0 = gettimeofday; T0: $ups[0] = Card(); $ups[1] = Card(); # draw cards for pos #0 & # +1 T1: if($ups[0] eq $ups[1]) { goto T0; } # cover a pair & go back if($ups[2] eq '0') { $ups[2] = Card(); } # fill pos#2 if vacant # check pos#2 against pos#0 - cover a pair & go back if($ups[2] eq $ups[0]) { Cover(2,0); goto T1; } # check pos#2 against pos#1 - cover a pair & go back if($ups[2] eq $ups[1]) { Cover(2,1); goto T1; } # watch the rows for pairs that don't get covered foreach my $x (@ups) { print "$x "; } print "\n"; for(my $j=3;$j<10;$j++) { if($ups[$j] eq '0') { $ups[$j] = Card(); } for(my $k=0;$k<$j;$k++) { if($ups[$j] eq $ups[$k]) { Cover($j,$k); goto T1; } } } + TX: print "\n"; foreach my $x (@ups) { print "$x "; } print "\t\tExit row\n"; my $del_t = (gettimeofday - $t0) * 10**6; my $usec = sprintf("%.2f",$del_t); print "\n$cntr passes in $usec microseconds. "; if($cntr == @deck) { print "Success!\n\n"; } else { print "{sigh...}\n\n"; } #======================================== sub Card { my $tmp = $deck[$cntr]; $cntr++; if($tmp ne 'X') { return $tmp; } else { goto TX; } } sub Suit { @suit = qw(A B C D E F G H I J K L M ); for(my $i=$#suit;$i>0;$i--) { my $j = int(rand($i + 1)); @suit[$i, $j] = @suit[$j, $i]; } } sub Deck { for(my $i=0;$i<4;$i++) { Suit(); @deck = (@deck,@suit); +} for(my $i=$#deck;$i>0;$i--) { my $j = int(rand($i + 1)); @deck[$i, $j] = @deck[$j, $i]; } push(@deck,"X"); } sub Cover { my $x = $_[0]; my $y = $_[1]; $ups[$x] = Card(); $ups[$y] = Card(); }

    pre tags replaced by Code tags by Grandfather

      Use <code> tags, otherwise array indices are turned into links.
      #!/usr/bin/perl use warnings; use strict; use Time::HiRes qw(gettimeofday tv_interval) +; my( @suit, @deck); my @ups = qw( 0 0 0 0 0 0 0 0 0 0); my $cntr = 0; Deck(); # four shuffled suits are added to a deck and then shuffled print "\n"; my $t0 = gettimeofday; T0: $ups[0] = Card(); $ups[1] = Card(); # draw cards for pos #0 & # +1 T1: if($ups[0] eq $ups[1]) { goto T0; } # cover a pair & go back if($ups[2] eq '0') { $ups[2] = Card(); } # fill pos#2 if vacant # check pos#2 against pos#0 - cover a pair & go back if($ups[2] eq $ups[0]) { Cover(2,0); goto T1; } # check pos#2 against pos#1 - cover a pair & go back if($ups[2] eq $ups[1]) { Cover(2,1); goto T1; } # watch the rows for pairs that don't get covered foreach my $x (@ups) { print "$x "; } print "\n"; for(my $j=3;$j<10;$j++) { if($ups[$j] eq '0') { $ups[$j] = Card(); } for(my $k=0;$k<$j;$k++) { if($ups[$j] eq $ups[$k]) { Cover($j,$k); goto T1; } } } + TX: print "\n"; foreach my $x (@ups) { print "$x "; } print "\t\tExit row\n"; my $del_t = (gettimeofday - $t0) * 10**6; my $usec = sprintf("%.2f",$del_t); print "\n$cntr passes in $usec microseconds. "; if($cntr == @deck) { print "Success!\n\n"; } else { print "{sigh...}\n\n"; } #======================================== sub Card { my $tmp = $deck[$cntr]; $cntr++; if($tmp ne 'X') { return $tmp; } else { goto TX; } } sub Suit { @suit = qw(A B C D E F G H I J K L M ); for(my $i=$#suit;$i>0;$i--) { my $j = int(rand($i + 1)); @suit[$i, $j] = @suit[$j, $i]; } } sub Deck { for(my $i=0;$i<4;$i++) { Suit(); @deck = (@deck,@suit); +} for(my $i=$#deck;$i>0;$i--) { my $j = int(rand($i + 1)); @deck[$i, $j] = @deck[$j, $i]; } push(@deck,"X"); } sub Cover { my $x = $_[0]; my $y = $_[1]; $ups[$x] = Card(); $ups[$y] = Card(); }

      I tried running the script 1000 times in 5.26.1 and the current blead (5.41.5-22-ga8a74a872d). There was no warning.

      map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]

        Your code is based on 4 normal goto plus one which is called inside Card() to leave the sub, which is called 4 more times, plus 2 times inside Cover() which is called thrice.

        These are 16 jumps in total.

        All this is "determined" on randomized data.

        Respect, you put the spaghetti into spaghetti code.

        That's highly unpredictable, and the missing code indentation doesn't make it easier to grasp.

        If your problems didn't or rarely happened before but are now, one reason might be differences in arithmetics.

        We just had a talk at the London Perl Workshop about an undiscovered bug only showing up if the same code and Perl version was run on a newer Ubuntu version.

        The precision jumped and the code crashed because a threshold was exceeded.

        Nota Bene: the code was already buggy before, but this went unnoticed.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        see Wikisyntax for the Monastery