tag:blogger.com,1999:blog-5560209661389175529.post3750655872511316079..comments2021-11-26T19:34:10.855+00:00Comments on Mechanical Sympathy: Write CombiningMartin Thompsonhttp://www.blogger.com/profile/15893849163924476586noreply@blogger.comBlogger49125tag:blogger.com,1999:blog-5560209661389175529.post-86536686100329952342019-05-13T08:28:52.151+01:002019-05-13T08:28:52.151+01:00Just ran this on my machine and got following resu...Just ran this on my machine and got following results when running the loops 10 times:<br />1 SingleLoop duration (ns) = 7074676009<br />1 SplitLoop duration (ns) = 4179656857<br />2 SingleLoop duration (ns) = 6974691458<br />2 SplitLoop duration (ns) = 4243834696<br />3 SingleLoop duration (ns) = 5057173801<br />3 SplitLoop duration (ns) = 4281710759<br />4 SingleLoop duration (ns) = 5053223285<br />4 SplitLoop duration (ns) = 3952401242<br />5 SingleLoop duration (ns) = 4739710461<br />5 SplitLoop duration (ns) = 4188487184<br />6 SingleLoop duration (ns) = 4761019124<br />6 SplitLoop duration (ns) = 4219472213<br />7 SingleLoop duration (ns) = 5078802967<br />7 SplitLoop duration (ns) = 3970636511<br />8 SingleLoop duration (ns) = 4778556539<br />8 SplitLoop duration (ns) = 4002392222<br />9 SingleLoop duration (ns) = 4764734738<br />9 SplitLoop duration (ns) = 3940427992<br />10 SingleLoop duration (ns) = 4931735291<br />10 SplitLoop duration (ns) = 3963758487<br /><br />I'm wondering why is the difference much smaller starting from the 3rd loop.<br /><br />Peter Cordes' comment is interesting and perhaps explains this somehow but I'm not sure I fully understand it O:-).<br /><br />PS: Running this on recent Mac Book pro with Intel core i7 2.6 Ghz 6 cores, 32 GB ram and JDK 12jumarhttps://www.blogger.com/profile/16287861292253074791noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-38209623297665289242019-02-26T17:17:37.740+00:002019-02-26T17:17:37.740+00:00This is an old blog and you are correct to point o...This is an old blog and you are correct to point out that what is happening is not that well explained. I am referring to the Line Fill Buffers which can be used for write combining on Intel CPUs, AMD separate them. I keep meaning to revisit my blog but struggle to find the time.Martin Thompsonhttps://www.blogger.com/profile/15893849163924476586noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-26499108790817113292019-02-26T06:49:32.678+00:002019-02-26T06:49:32.678+00:00I'm pretty sure this explanation for the obser...I'm pretty sure this explanation for the observed performance isn't right.<br /><br />"write combining" to write-back memory happens either inside the store buffer (for back-to-back stores to the same line), or by L1d itself *being* the buffer: a line stays hot in Modified state while multiple stores commit to it, so it only needs to be written back once.<br /><br />The performance effect you're seeing (and which Intel's optimization manual recommends avoiding by splitting loops with more than 4 output streams) is more likely from conflict misses when lines are evicted from L1d while there are still pending writes to them. How can this happen? L1d is 8-way associative.<br /><br />But L1d replacement is only pseudo-LRU. True LRU takes significantly more bits per set to track to LRU state of 8 ways, so my understanding is that pseudo-LRU is common.<br /><br />---<br /><br />In any case, you seem to be talking about the LFBs (Line Fill Buffers). Nehalem has 10 of them, same as later CPUs.<br /><br />LFBs (instead of L1d lines) are used for write-combining of NT stores, or I think stores to WC memory. That's only because they have to bypass cache.<br /><br /> That's where the limit of 4 maybe comes in, although I Nehalem can use all 10 of its LFBs as WC buffers like SnB-family CPUs can. Still, they're also needed for incoming lines and regular write-back to L2, so unless NT stores are *all* you're doing, it's definitely best to do all the stores for a single line back-to-back in the right order.<br /><br />But your microbench doesn't do anything but normal stores.<br /><br />So the mechanism you're proposing as the cause for this effect just doesn't make sense.<br /><br />See also discussion on Stack Overflow: https://stackoverflow.com/questions/53435632/are-write-combining-buffers-used-for-normal-writes-to-wb-memory-regions-on-intel#comment96160262_53435632 The actual question is asking about this, and the answer is a similar microbenchmark. The conclusions are questionable, but the performance counter results are maybe interesting. (Still, neither BeeOnRope nor I are convinced that it's actually demonstrating use of LFBs for write-combining of normal stores.)<br /><br />And in any case, that's about combining in an LFB while waiting for a cache line to arrive. You're talking about somehow combining something before/during write-back from L1 or L2 to L3. That just makes no sense; there's nothing to combine with, it's already a full line write-back.Anonymoushttps://www.blogger.com/profile/00752566565358703027noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-86942248475096360232018-03-31T22:25:51.919+01:002018-03-31T22:25:51.919+01:00Here is slightly generalized C-based version:
http...Here is slightly generalized C-based version:<br />https://github.com/artpol84/poc/tree/master/benchmarks/write_combineAnonymoushttps://www.blogger.com/profile/12822319314003610252noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-17308749754545651752018-03-31T22:25:05.617+01:002018-03-31T22:25:05.617+01:00Here is slightly generalized C version with some r...Here is slightly generalized C version with some results:<br />https://github.com/artpol84/poc/tree/master/benchmarks/write_combineAnonymoushttps://www.blogger.com/profile/12822319314003610252noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-64433136683402956052016-03-06T15:25:48.993+00:002016-03-06T15:25:48.993+00:00Intel processors now have 10 LF/WC buffers so this...Intel processors now have 10 LF/WC buffers so this is not such an issue any longer.Martin Thompsonhttps://www.blogger.com/profile/15893849163924476586noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-21049436890849358222015-11-17T16:23:53.998+00:002015-11-17T16:23:53.998+00:00Hmm... I wrote a C++ version which demonstrates ~2...Hmm... I wrote a C++ version which demonstrates ~2.5x improvement for split loop:<br /> 1 SingleLoop duration (ns) = 12139922244<br /> 1 SplitLoop duration (ns) = 4732561921<br /> 2 SingleLoop duration (ns) = 12129320126<br /> 2 SplitLoop duration (ns) = 4777225938<br /> 3 SingleLoop duration (ns) = 12126297712<br /> 3 SplitLoop duration (ns) = 4716507099<br /> result = 21<br /><br />However, Java performs well in both cases (note only ~10% performance hit by Java compared to the best case in C++):<br /> 1 SingleLoop duration (ns) = 5311133217<br /> 1 SplitLoop duration (ns) = 5054977738<br /> 2 SingleLoop duration (ns) = 5090976210<br /> 2 SplitLoop duration (ns) = 5276584630<br /> 3 SingleLoop duration (ns) = 5219806807<br /> 3 SplitLoop duration (ns) = 5931649956<br /> result = 21<br /><br /><br />Does modern JIT smartly compile away the difference?<br /><br /><br />Platform: Fedora 23 x86_64, Intel Core i5-4460, 8GB, openjdk-1.8.0.65<br />C++ sources: https://gist.github.com/uvsmtid/52caa3f2cfab287b2b80<br />Alexey Pakseykinhttps://www.blogger.com/profile/12800285702468565110noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-73174648204252265902015-11-17T16:00:01.922+00:002015-11-17T16:00:01.922+00:00Hmm... I compared C++ version with Java (and see 1...Hmm... I compared C++ version with Java (and see 10% C++ improvement in the best/3-arrays version). <br /><br />However both cases in Java are almost the same - no performance hit.<br />Does JIT optimize away the difference?<br /><br />Platform: Fedor 23 x86_64, Intel® Core™ i5-4460, openjdk-1.8.0.65-3.b17, Java, gcc-c++-5.1.1<br />C++ source: https://gist.github.com/uvsmtid/52caa3f2cfab287b2b80Alexey Pakseykinhttps://www.blogger.com/profile/12800285702468565110noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-24588706080912957842015-09-14T09:53:37.702+01:002015-09-14T09:53:37.702+01:00thank you so much for the information. this blog i...thank you so much for the information. this blog is very interesting in a geeky way.<br /><br />i have read a post somewhere saying that write combining buffer is not worth for application programmers looking into anymore and also it has been renamed/replaced with fill buffer to reflect its change of function.<br /><br />also I have tested it on my i7 2640M laptop, I don't see any performance gain either, rather a performance degradation in split loop case tests.<br />Kin Cheunghttps://www.blogger.com/profile/18336227357910248392noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-12879586542277932462014-06-03T10:32:31.969+01:002014-06-03T10:32:31.969+01:00The latest Intel CPU now have 10 write combining b...The latest Intel CPU now have 10 write combining buffer so the effect is much less pronounced. Other processors such as AMD can have less.Martin Thompsonhttps://www.blogger.com/profile/15893849163924476586noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-24186971751580958422014-06-03T10:08:13.275+01:002014-06-03T10:08:13.275+01:00It is impressive to see your!
I can see ~10% impr...It is impressive to see your! <br />I can see ~10% improvement i7-2630QM Win7 x64. As you mentioned thread may compete with each other, but anyway there is some benefit in applying the technique even on threaded Intel CPU.Pranashttps://www.blogger.com/profile/04177160253878809806noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-59297778521749547782014-01-19T18:29:52.943+00:002014-01-19T18:29:52.943+00:00The content of the WC buffer does not wait to fill...The content of the WC buffer does not wait to fill before writing. It is written to the cacheline as soon as it is available. A WC buffer is 64 bytes, i.e. the size of a cache line.<br /><br />You have only 4 WC buffers per core. Therefore you can only write to 4 distinct locations that reside in different cachelines, if those cache lines are not in the L1/L2 caches.Martin Thompsonhttps://www.blogger.com/profile/15893849163924476586noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-45742980533949551132014-01-13T13:34:59.105+00:002014-01-13T13:34:59.105+00:00it's something related to the fact that you ha...it's something related to the fact that you have on your processor pc only 4 buffers and you can write from max four distinct memory zones to your WC buffers ?Anonymoushttps://www.blogger.com/profile/14979787102337446172noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-2917499264200952442014-01-13T13:31:07.050+00:002014-01-13T13:31:07.050+00:00Hi Martin,
great post; a lot of useful informati...Hi Martin, <br /><br />great post; a lot of useful information can be find on this blog.<br /><br />I have a question related to write combining process. all information that have to be written in memory in case of a cache miss(L1 or L2) will be grouped and write only when WC buffer is fill up with data, this bring us an important improvement of latency, beside to write each change in a cache line. What is not clear to me in the previous code when you write only three array elements inside of while loop, when WC buffer will be fill with information it will be write to memory, right(after each loop we write to WC buffer 3 bytes and from what I know the size of this buffer is ~ 32 bytes, this mean only after ~ ten loops will be filled buffer with data, if my logic is correct)? if yes what is the difference when you loop against all six array elements in the same loop ? <br />I misunderstand something ?Anonymoushttps://www.blogger.com/profile/14979787102337446172noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-90601817761260909202013-12-30T18:31:56.955+00:002013-12-30T18:31:56.955+00:00There are some issues with this test on more recen...There are some issues with this test on more recent processors. I plan to redo this blog and bring it up to date.Martin Thompsonhttps://www.blogger.com/profile/15893849163924476586noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-46593976211859015332013-12-30T18:24:16.939+00:002013-12-30T18:24:16.939+00:00Hi Martin, thanks a lot for this blog:
I am runni...Hi Martin, thanks a lot for this blog:<br /><br />I am running this program in my MacBook Pro which has the following specs:<br /><br /> Processor Name: Intel Core i7<br /> Processor Speed: 2.9 GHz<br /> Number of Processors: 1<br /> Total Number of Cores: 2<br /> L2 Cache (per Core): 256 KB<br /> L3 Cache: 4 MB<br /> Memory: 8 GB<br /><br />These are the results i am getting:<br /><br />1 SingleLoop duration (ns) = 5051671000<br />1 SplitLoop duration (ns) = 6574749000<br />2 SingleLoop duration (ns) = 4806397000<br />2 SplitLoop duration (ns) = 5931679000<br />3 SingleLoop duration (ns) = 4786564000<br />3 SplitLoop duration (ns) = 5521178000<br />result = 21<br /><br />I am seeing that the single loop is actually faster than the split loop, how can this be possible?<br /><br />Thanks a lot,<br />Carlos.Carlos Curottohttps://www.blogger.com/profile/02561563392896286497noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-10063675470984122522013-09-04T04:19:50.583+01:002013-09-04T04:19:50.583+01:00Hi Martin,
Thanks for this really useful article. ...Hi Martin,<br />Thanks for this really useful article. Could you please clarify two things for me,<br />Is there a reason for assigning arrays in a reverse order and I don't get how separating the assigning part into two loops helps on using wc store buffers efficiently. I mean how does the splitting helps in flushing the buffers ? <br /> Prabathhttps://www.blogger.com/profile/01336462152917820046noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-50136818295016771502013-03-19T11:08:03.712+00:002013-03-19T11:08:03.712+00:00I cannot speak for how the __iowrite64_copy() func...I cannot speak for how the __iowrite64_copy() function is implemented. Have you looked at the generated assembler? Just Google for the MOVNTDQ instruction :-)Martin Thompsonhttps://www.blogger.com/profile/15893849163924476586noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-6909458854036076412013-03-19T10:56:31.938+00:002013-03-19T10:56:31.938+00:00Yes access is aligned. Sorry, but could you pls el...Yes access is aligned. Sorry, but could you pls elaborate more on this MOVNTDQ instruction? The __iowrite64_copy routine in the Linux kernel is s'pposed to help in the combining ,if my guess on what you are referring to is correct?smirnonhttps://www.blogger.com/profile/16721602235840469009noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-11722774348304754452013-03-19T10:50:37.003+00:002013-03-19T10:50:37.003+00:00I believe you need to use MOVNTDQ to have streamin...I believe you need to use MOVNTDQ to have streaming writes to WC memory and get them combined. Also are you ensuring aligned access?Martin Thompsonhttps://www.blogger.com/profile/15893849163924476586noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-20698753705127165442013-03-19T09:42:48.640+00:002013-03-19T09:42:48.640+00:00Thanks for your reply. Yes so i copy 64 bytes to a...Thanks for your reply. Yes so i copy 64 bytes to a particular memory location on this WC mapped BAR/area. Ideally these should have gone out on the PCIe Bus as just one big 64 byte PCIe TLP/Packet, but i see 8 packets of 64-bit each going out ,which indicates combining hasn't kicked in. <br />smirnonhttps://www.blogger.com/profile/16721602235840469009noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-3261468850565218592013-03-19T08:30:55.097+00:002013-03-19T08:30:55.097+00:00My experience of kernel drivers is very dated now....My experience of kernel drivers is very dated now. It sounds like you want to set the memory type to WC. This blog refers to how the WC buffer are using with write-back memory. I'd need to much better understand the issue you are seeing before I could give advice.<br /><br />How do you know the writes to the same cache line are not being combined? Martin Thompsonhttps://www.blogger.com/profile/15893849163924476586noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-29110947794199967602013-03-18T08:25:11.728+00:002013-03-18T08:25:11.728+00:00Martin,
Great Post!! I want to know how does this...Martin,<br /> Great Post!! I want to know how does this work in the case of kernel drivers however, would you happen to know that? THat is , i have a BAR region on my adapter that i can map in WC mode using ioremap_wc() in the linux kernel. And then use a routine like iowrite_64_copy() to copy the data onto this mapped area. However this does not always do the combining for me!<br /><br />Is there a possibility that if the system is idle , it just sends out 64-bit/8 byte writes as it recieves them instead of combining them into 1 big 64-byte write ?smirnonhttps://www.blogger.com/profile/16721602235840469009noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-26472291606577878862012-07-04T09:18:03.830+01:002012-07-04T09:18:03.830+01:00Sorry for the slow response getting lost in my inb...Sorry for the slow response getting lost in my inbox. Write Combines happen for a combined cache-miss on L1 and L2. If you follow the code above, the arrays are sufficiently large so they do not fit in combined L1 & L2 caches. L2 is not inclusive or exclusive with L1 on Nehalem onwards. Think of L2 as a staging area between L1 and L3 to reduce each core beating on the L3. L1 and L2 for each core is inclusive in the L3.<br /><br />If an existing line in the L1 and L2 combination needs evicted to L3 then it may only need written to L3 and is thus not always a write-back to main memory.<br /><br />From Java you have no control over the type of memory such as write-back, write-through, or write-combining, etc. For Java everything is write-back and all goes via the cache. Write combines as discussed here is different from enabling the write-combining memory type, that requires ASM.Martin Thompsonhttps://www.blogger.com/profile/15893849163924476586noreply@blogger.comtag:blogger.com,1999:blog-5560209661389175529.post-52541341839245065192012-06-07T06:19:16.838+01:002012-06-07T06:19:16.838+01:00Hi Martin,
I found this blog very very in...Hi Martin,<br /> I found this blog very very interesting,I have a question, write-combines happen only when there is a cache miss at L1 or else write-backs happen? If yes, to utilize the optimization of write-combines how do we make sure/guarantee there is a cache miss? And any ideas about using write-throughs ? please correct me if I'm completely wrong.<br /><br />thanksAnonymousnoreply@blogger.com