dyllongagnier
2021-1-24 09:21:50

I did some benchmarking today and found that one of the fastest ways to support iteration is surprisingly just to have the equivalent of for-each. i.e., a procedure which takes in a thunk to call on each element of the iteration. It was particularly efficient if said for-each function was inlined by defining it using define-inline or begin-encourage-inline. However, even without inlining, it was still much faster than a generator and about as fast as as a custom sequence using make-do-sequence.

This is actually a reasonable approach because the main thing missing with for-each iteration is early termination, but that is easily solved in Racket by an escape continuation which is pretty fast since you only call the continuation once for the entire for-each loop.

I initially thought this was an interesting concept because writing a for-each style function is incredibly easy, arguably even easier than a generator since you don’t need to use any special forms. The main disadvantage of this approach outside of Racket is that you can’t pause iteration, but if you really want to do that in Racket you can just use a full delimited continuation.


dyllongagnier
2021-1-24 09:25:09

Note that the reason you want inlining of for-each is so that the thunk you pass it can in turn be inlined. This was with Racket CS, but there still is a measurable cost for dynamically calling a function.


laurent.orseau
2021-1-24 09:58:39

That’s precisely what for loops are for :)


laurent.orseau
2021-1-24 09:59:25

It has lots of features like #:break and variants like for/fold


laurent.orseau
2021-1-24 10:00:21

(i realize i may be at the wrong level of answers, sorry : D )


laurent.orseau
2021-1-24 10:01:50

Buy I found in my old benchmarks that for loops are often at least as fast as custom named let loops (which are supposed to be ‘lower’ level)


dyllongagnier
2021-1-24 21:20:38

@laurent.orseau yeah, for loops are the best option for built in data structure. My methodology mostly makes sense if you need to write some new iteration strategy like iterating over a custom tree or something. The reason for loops are fast is because of custom support from the for macro at expansion time for stuff like lists, vectors, ranges, etc. I think you can add support for new iterators to the for macro, but it looks very complicated to do and may not be worth the effort in a lot of cases.

For example, maybe you want to church encode some data which requires enumerating the prime numbers. You could just write that as a custom for-each/fold function which would seemingly give you most of the performance of the custom for loop while being much simpler to write.


laurent.orseau
2021-1-24 22:18:00

For such cases wouldn’t a named let loop would be just as simple, while directly avoiding the lambda?


laurent.orseau
2021-1-24 22:19:51

Ah, or you mean that the inlined lambda is what you provide the user with, and they just have to write the for-each?