<div class="gmail_quote"><div class="gmail_quote"><div class="im">2011/4/4 Siarhei Siamashka <span dir="ltr">&lt;<a href="mailto:siarhei.siamashka@gmail.com" target="_blank">siarhei.siamashka@gmail.com</a>&gt;</span><br></div>
<div><div></div><div class="h5"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><br></div>
So what I&#39;m going to say? If we were to release pixman as it is today<br>
(the code from the current pixman git master), then some of the<br>
bilinear scaling cases would become a lot faster, but still not cover<br>
everything. And also the users of cairo/pixman might find it<br>
beneficial to explicitly split complex operations into separate steps,<br>
which is a bad idea in the long run. One more part of the picture is<br>
the &quot;fetch -&gt; combine -&gt; store&quot; general pipeline. If we provide a<br>
really well optimized NEON bilinear fetcher for this pipeline, then at<br>
least the performance will be reasonable for a lot of operations which<br>
involve bilinear scaling. And after this, any single pass fast path<br>
will have to compete against general pipeline + NEON bilinear fetcher,<br>
which might still be a challenge.<br><br></blockquote><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Regarding the &quot;fetch -&gt; combine -&gt; store&quot; pipeline, I have attached a<br>
simple benchmark program, which tries to estimate the overhead of<br>
using intermediate temporary buffer. Here are some of the results:<br>
<br>
 == Nokia N900 ==<br>
<br>
 OMAP3430, ARM Cortex-A8 r1p3 @500MHz, 32-bit LPDDR @166MHz:<br>
   direct copy:                    228.191 MB/s<br>
   direct copy prefetched:         365.788 MB/s<br>
   copy via tmp buffer:            154.853 MB/s<br>
   copy via tmp buffer prefetched: 238.304 MB/s<br>
   tmp buffer use slowdown ratio:  1.53x<br>
<br>
 OMAP3430, ARM Cortex-A8 r1p3 @600MHz, 32-bit LPDDR @166MHz:<br>
   direct copy:                    242.512 MB/s<br>
   direct copy prefetched:         398.767 MB/s<br>
   copy via tmp buffer:            174.982 MB/s<br>
   copy via tmp buffer prefetched: 276.585 MB/s<br>
   tmp buffer use slowdown ratio:  1.44x<br>
<br>
 == Samsung Galaxy Tab ==<br>
<br>
 S5PC110, ARM Cortex-A8 r2p2 @1GHz, 32-bit LPDDR @200MHz:<br>
   direct copy:                    643.620 MB/s<br>
   direct copy prefetched:         627.381 MB/s<br>
   copy via tmp buffer:            671.489 MB/s<br>
   copy via tmp buffer prefetched: 640.335 MB/s<br>
   tmp buffer use slowdown ratio:  0.96x<br>
<br>
As can be seen, an early revision of ARM Cortex-A8 core from OMAP3430<br>
SoC used in Nokia N900 had a significant penalty for this type memory<br>
access pattern. So my older recommendation had always been &quot;use single<br>
pass processing at any cost&quot;. Investigating it a bit more, looks like<br>
memory bandwidth is maximized only when both read and write operations<br>
with memory are happening at the same time, kind of &quot;full-duplex&quot;<br>
behavior. And overall, the performance seems to be very far from<br>
utilizing memory controller 100% there. Not to mention the other WTF<br>
questions such as why prefetch degraded performance in some cases of<br>
nearest scaling or why NEON makes memcpy faster. But last generation<br>
chips seem to have improved really a lot. Both TI OMAP3630/DM3730 and<br>
Samsung S5PC110 show that temporary buffer is not a problem anymore,<br>
Samsung being just a bit faster overall (that&#39;s why I like to use it<br>
for all my latest benchmarks).<br>
<br>
So right now, based on the numbers from modern Cortex-A8 devices, I<br>
would not say that single pass processing is a clear winner anymore.<br>
Though I also would not say that &quot;fetch -&gt; combine -&gt; store&quot; is the<br>
way to go now. One example: for this particular bilinear<br>
over_8888_8888 operation, if we split it into fetch and combine<br>
stages, then bilinear fetch is going to be CPU limited and combine<br>
part is going to be memory limited. Such uneven load on memory/cpu may<br>
make single pass processing a bit more favorable.<br></blockquote><div><br></div></div></div><div>Cairo actually uses many temp buffers and image objects to make problems easier.</div>
<div>Each of these operations will call several malloc/free which will cause serious inter-thread contention.</div><div>Although well implemented memory management and caching can reduce this overhead, I want to avoid these temp approaches if possible.</div>

<div>Well, maybe temp buffers for scanline (not entire image) are reasonable and easy to go in cache mechanism.</div><div>But it&#39;s not that simple problem in case of multi-thread.</div><div>In this point of view, single-pass is always favorable if it beats general fetch-&gt;combine-&gt;store pipeline in performance.</div>

<div>Because it does not require alloc/free of temp buffers.</div><div>Single-pass can be a good choice for small sized image composition due to this.</div><div><br></div><div>Fetch-&gt;combine-&gt;store is relatively simple to implement reusing already existing fast path scanline functions.</div>

<div>And it will give us reasonable performance and maximize code utilization.</div><div>And it can also give us overlap-aware behavior in some simple cases.</div><div><br></div><div>Anyway we have to consider both(single-pass vs general) for various CPUs and I&#39;ve got that in mind now.</div>

<div>Thanks for advice.</div><div><br></div><div>Another interesting thing I recently found is that reading memory location recently written causes performance decrease.</div><div>pixman_blt(src, dst...) with src != dst (far apart enough) gave me 350 MPix/s on S5PC110 with 32 bpp.</div>

<div>But if src == dst or src and dst are close, it dropped to 180 MPix/s.</div><div>If src and dst are more far apart, performance was increased.</div><div>I searched out many ARM documents but can&#39;t find any clues on this behavior.</div>

<div>First I think later case will cause more cache hit and should be much faster.</div><div>But I was wrong.</div><div>Maybe write allocate mode with L2 cache or write buffer can be a reason for this.</div><div>I figured out this while implementing overlap-aware blt function.</div>

<div>Any idea?</div><div><br></div><div>I attached my test code.</div><div> </div></div>-- <br>Best Regards,<div>Taekyun Kim</div></div>