go ahead... be a heretic PerlMonks

### Re: Variable number of foreach loops (improving performance)

by smls (Friar)
 on Dec 11, 2013 at 11:35 UTC Need Help??

in reply to Variable number of foreach loops

Here are some attempts at improving performance, compared to the solutions already posted (of which BrowserUK's recursive solution was the fastest):

### Branching solution

A trivial (but non-elegant) way to gain performance is to directly implement the nested loops for a few (presumably common) values of n, using if/elsif/else to branch between them, and only fall back to the general-case recursive implementation for higher values of n:

```sub multi_foreach(&@) {
my \$code = shift;
if (@_ == 1) {
for my \$i (@{ shift() }) {
\$code->( \$i );
}
}
elsif (@_ == 2) {
my (\$a0, \$a1) = @_;
for my \$i0 (@\$a0) {
for my \$i1 (@\$a1) {
\$code->( \$i0, \$i1 );
}
}
}
elsif (@_ > 2) {
my (\$a0, \$a1, \$a2, @rest) = @_;
for my \$i0 (@\$a0) {
for my \$i1 (@\$a1) {
for my \$i2 (@\$a2) {
if (@rest) {
&multi_foreach_recursive( \$code, scalar @rest,
@rest, \$i0, \$i1, \$i2 );
}
else { \$code->( \$i0, \$i1, \$i2 ); }
}
}
}
}
}

multi_foreach { say join ' ', @_ } \( @a, @b, @c );

### eval solution

The logical generalization of the branching solution, would be to dynamically generate the nested foreach loops for arbitrary levels of nesting. This incurs an overhead for code-generation each time the algorithm is called (PS: caching could help), but once generated the actual looping code will run very fast. Thus this solution tends to be slower than the recursive solution for small inputs, but faster for large inputs (i.e. many big arrays):

```sub multi_foreach(&@) {
my \$code = shift;
my (\$head, \$inside, \$tail) = ('', '\$code->(', ')');
foreach (0..\$#_) {
\$head   .= "for my \\$i\$_ (\@{\\$_[\$_]}) { ";
\$inside .= "\\$i\$_, ";
\$tail   .= ' }';
}
eval( \$head . \$inside . \$tail );
}

multi_foreach { say join ' ', @_ } \( @a, @b, @c );

Replies are listed 'Best First'.
Re^2: Variable number of foreach loops (improving performance)
by tobyink (Canon) on Dec 11, 2013 at 11:47 UTC

You'll gain a lot more speed rewriting it in XS and using the multicall interface to call \$code.

use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name

Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1066607]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others cooling their heels in the Monastery: (5)
As of 2024-08-10 20:18 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
When will the AI bubble burst?

Results (22 votes). Check out past polls.

Notices?
 • erzuuli ‥ 🛈The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.