Arc Forumnew | comments | leaders | submitlogin
A faster 'in
6 points by conanite 2985 days ago | 4 comments
My humble parser.arc relies on 'in to detect whitespace

  (def whitespace? (ch)
    (if (in ch #\space #\newline #\tab #\return) ch))
Inspecting the macro-expansion of this code one day, I figured it could run faster. 'in expands to an 'or form, which in turn expands to a bazillion nested function closures. But conceptually, all I need is

  (def whitespace? (ch)
    (if (is ch #\space ) ch
        (is ch #\newline ) ch
        (is ch #\tab ) ch
        (is ch #\return ) ch))
So here's a faster 'in

  (mac faster-in (x . choices)
    (w/uniq g
      `(let ,g ,x
         (if ,@(mappend (fn (c) `((is ,g ,c) ,g)) choices)))))
Performance:

  arc> (time10 (repeat 43670 (in #\x #\space #\newline #\tab #\return)))
  time: 642 msec.
  nil
  arc> (time10 (repeat 43670 (faster-in #\x #\space #\newline #\tab #\return)))
  time: 607 msec.
  nil
the 43670 is the size of arc.arc, hence faster-in should speed up parsing that file by about 4msec ... about 1.25% of total parsing time, so it's not going to change the world or anything.

This is the original 'in, for comparison:

  (mac in (x . choices)
    (w/uniq g
      `(let ,g ,x
         (or ,@(map1 (fn (c) `(is ,g ,c)) choices)))))
One difference: 'faster-in returns the found value, rather than 't, which in my case at least, is more useful.

(parser.arc is with rainbow these days rather than anarki, if you're interested it's at http://github.com/conanite/rainbow/blob/master/src/arc/lib/parser.arc )



4 points by rntz 2984 days ago | link

I'd be interested to see an actual comparison of the time it takes to parse some example files with the two different versions of 'whitespace?. Why use microbenchmarks when you can test on real-world examples?

> One difference: 'faster-in returns the found value, rather than 't.

What if that value is nil? That makes for a nasty corner case.

-----

2 points by conanite 2983 days ago | link

a faster 'in, without the nasty corner case:

  (mac faster-in (x . choices)
    (w/uniq g
      `(let ,g ,x
         (if ,@(mappend (fn (c) `((is ,g ,c) t)) choices)))))
it's a tiny difference, and if you know in advance you're not looking for nil, the other version is just as good.

-----

1 point by conanite 2984 days ago | link

What if that value is nil? That makes for a nasty corner case.

oops, yes, you're right, good point

Why use microbenchmarks

using parser.arc to index tokens from arc.arc is the benchmark I use most often for tuning rainbow performance. I wanted to present the issue with as few extraneous details as possible.

-----

2 points by tc-rucho 2982 days ago | link

Micro benchmarks can be misleading sometimes. Most of the time I get different timings for the same microbenchmark when I eval it a few times so it's really difficult to see if they really do their job when the timings are too close unless some full blown benchmark is performed on some real data.

-----