<div class="gmail_quote">On Wed, Dec 22, 2010 at 12:05 PM, Francesc Alted <span dir="ltr"><<a href="mailto:faltet@pytables.org">faltet@pytables.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
<div class="im"><snip><br>
<br>
</div>Ah, things go well now:<br>
<br>
>>> timeit 3*a+b-(a/c)<br>
10 loops, best of 3: 67.7 ms per loop<br>
<div class="im">>>> timeit luf(lambda a,b,c:3*a+b-(a/c), a, b, c)<br>
</div>10 loops, best of 3: 27.8 ms per loop<br>
<div class="im">>>> timeit ne.evaluate("3*a+b-(a/c)")<br>
10 loops, best of 3: 42.8 ms per loop<br>
<br>
</div>So, yup, I'm seeing the good speedup here too :-)<br></blockquote><div> </div><div>Great!</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
<div class="im"><snip><br>
<br>
</div>Well, see the timings for the non-broadcasting case:<br>
<div class="im"><br>
>>> a = np.random.random((50,50,50,10))<br>
</div>>>> b = np.random.random((50,50,50,10))<br>
>>> c = np.random.random((50,50,50,10))<br>
<br>
>>> timeit 3*a+b-(a/c)<br>
10 loops, best of 3: 31.1 ms per loop<br>
<div class="im">>>> timeit luf(lambda a,b,c:3*a+b-(a/c), a, b, c)<br>
</div>10 loops, best of 3: 24.5 ms per loop<br>
>>> timeit ne.evaluate("3*a+b-(a/c)")<br>
100 loops, best of 3: 10.4 ms per loop<br>
<br>
However, the above comparison is not fair, as numexpr uses all your<br>
cores by default (2 for the case above).  If we force using only one<br>
core:<br>
<br>
>>> ne.set_num_threads(1)<br>
>>> timeit ne.evaluate("3*a+b-(a/c)")<br>
100 loops, best of 3: 16 ms per loop<br>
<br>
which is still faster than luf.  In this case numexpr was not using SSE,<br>
but in case luf does so, this does not imply better speed.</blockquote><div><br></div><div>Ok, I get pretty close to the same ratios (and my machine feels a bit slow...):</div><div><br></div><div>In [6]: timeit 3*a+b-(a/c)</div>
<div>10 loops, best of 3: 101 ms per loop</div><div><br></div><div>In [7]: timeit luf(lambda a,b,c:3*a+b-(a/c), a, b, c)</div><div>10 loops, best of 3: 53.4 ms per loop</div><div><br></div><div>In [8]: timeit ne.evaluate("3*a+b-(a/c)")</div>
<div>10 loops, best of 3: 27.8 ms per loop</div><div><br></div><div>In [9]: ne.set_num_threads(1)</div><div>In [10]: timeit ne.evaluate("3*a+b-(a/c)")</div><div>10 loops, best of 3: 33.6 ms per loop</div><div><br>
</div><div>I think the closest to a "memcpy" we can do here would be just adding, which shows the expression evaluation can be estimated to have 20% overhead.  While that's small compared the speedup over straight NumPy, I think it's still worth considering.</div>
<div><br></div><div>In [11]: timeit ne.evaluate("a+b+c")</div><div>10 loops, best of 3: 27.9 ms per loop</div><div><br></div><div>Even just switching from add to divide gives more than 10% overhead.  With SSE2 these divides could be done two at a time for doubles or four at a time for floats to cut that down.</div>
<div><br></div><div>In [12]: timeit ne.evaluate("a/b/c")</div><div>10 loops, best of 3: 31.7 ms per loop</div><div><br></div><div>This all shows that the 'luf' Python interpreter overhead is still pretty big, the new iterator can't defeat numexpr by itself.  I think numexpr could get a nice boost from using the new iterator internally though - if I go back to the original motivation, different memory orderings, 'luf' is 10x faster than single-threaded numexpr.</div>
<div><br></div><div><div>In [15]: a = np.random.random((50,50,50,10)).T</div><div>In [16]: b = np.random.random((50,50,50,10)).T</div><div>In [17]: c = np.random.random((50,50,50,10)).T</div><div><br></div><div>In [18]: timeit ne.evaluate("3*a+b-(a/c)")</div>
<div>1 loops, best of 3: 556 ms per loop</div><div><br></div><div>In [19]: timeit luf(lambda a,b,c:3*a+b-(a/c), a, b, c)</div><div>10 loops, best of 3: 52.5 ms per loop</div></div><div><br></div><div><br></div><div>Cheers,<br>
Mark</div></div>