<story><title>tolower() in bulk at speed</title><url>https://dotat.at/@/2022-06-27-tolower-swar.html</url></story><parent_chain><item><author>dragontamer</author><text>Seems a bit odd to stop at SWAR on today&amp;#x27;s systems.&lt;p&gt;Every system I&amp;#x27;m aware of has 128-bit SIMD implemented: either SSE (x86), NEON (ARM), or AltiVec (POWERPC). As such, 128-bit SIMD is the &amp;quot;reliably portable&amp;quot; SIMD operation.&lt;p&gt;Of course, for fastest speeds, you need to go to the largest SIMD-register size for your platform: 512-bit for some Intel processors, rumored AMD Zen4 and Centaur chips. 256-bit for most Intel &amp;#x2F; AMD Zen3 chips. 128-bit for Apple ARMs, 512-bit for Fujitsu A64 ARMs, etc. etc.&lt;p&gt;&amp;gt; And these are the fastest kinds of instructions :-)&lt;p&gt;And it should be noted that modern SIMD instructions execute at one-per-clock-tick. So the 64-bit instructions certainly are fast, but the SIMD instructions tie if you&amp;#x27;re using simple XOR or comparison operations.&lt;p&gt;--------&lt;p&gt;This style of code might even be &amp;quot;reliably auto-vectorizable&amp;quot; on GCC and CLANG actually. I wonder if I could get portable auto-vectorizing C from these examples.</text></item></parent_chain><comment><author>dietrichepp</author><text>Naive version to explore autovectorization:&lt;p&gt;&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;gcc.godbolt.org&amp;#x2F;z&amp;#x2F;bcefvznG3&quot; rel=&quot;nofollow&quot;&gt;https:&amp;#x2F;&amp;#x2F;gcc.godbolt.org&amp;#x2F;z&amp;#x2F;bcefvznG3&lt;/a&gt;&lt;p&gt;Yes, there&amp;#x27;s some amount of autovectorization here. Seems like a mess of a function. Here&amp;#x27;s something more like tolower8():&lt;p&gt;&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;gcc.godbolt.org&amp;#x2F;z&amp;#x2F;h8GTanodd&quot; rel=&quot;nofollow&quot;&gt;https:&amp;#x2F;&amp;#x2F;gcc.godbolt.org&amp;#x2F;z&amp;#x2F;h8GTanodd&lt;/a&gt;&lt;p&gt;The generated code definitely looks funny to me. Here is a manual vectorization, which is shorter:&lt;p&gt;&lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;gcc.godbolt.org&amp;#x2F;z&amp;#x2F;1e4odEsKq&quot; rel=&quot;nofollow&quot;&gt;https:&amp;#x2F;&amp;#x2F;gcc.godbolt.org&amp;#x2F;z&amp;#x2F;1e4odEsKq&lt;/a&gt;&lt;p&gt;The issue of loading into your vector register... well, there are some dirty tricks for that. The 16-byte slice containing the first byte, and the 16-byte slice containing the last byte, can both be loaded into registers and then shifted around in order to construct the desired value. Note the careful wording here... these slices might be the same slice. Or you can iterate over the 16-byte slices containing the array, and shift as you go, if you&amp;#x27;re storing into a different location. Or you can use various masked load&amp;#x2F;store operations on various architectures.</text></comment>
<story><title>tolower() in bulk at speed</title><url>https://dotat.at/@/2022-06-27-tolower-swar.html</url></story><parent_chain><item><author>dragontamer</author><text>Seems a bit odd to stop at SWAR on today&amp;#x27;s systems.&lt;p&gt;Every system I&amp;#x27;m aware of has 128-bit SIMD implemented: either SSE (x86), NEON (ARM), or AltiVec (POWERPC). As such, 128-bit SIMD is the &amp;quot;reliably portable&amp;quot; SIMD operation.&lt;p&gt;Of course, for fastest speeds, you need to go to the largest SIMD-register size for your platform: 512-bit for some Intel processors, rumored AMD Zen4 and Centaur chips. 256-bit for most Intel &amp;#x2F; AMD Zen3 chips. 128-bit for Apple ARMs, 512-bit for Fujitsu A64 ARMs, etc. etc.&lt;p&gt;&amp;gt; And these are the fastest kinds of instructions :-)&lt;p&gt;And it should be noted that modern SIMD instructions execute at one-per-clock-tick. So the 64-bit instructions certainly are fast, but the SIMD instructions tie if you&amp;#x27;re using simple XOR or comparison operations.&lt;p&gt;--------&lt;p&gt;This style of code might even be &amp;quot;reliably auto-vectorizable&amp;quot; on GCC and CLANG actually. I wonder if I could get portable auto-vectorizing C from these examples.</text></item></parent_chain><comment><author>sedatk</author><text>&amp;quot;labels are frequently less than 8 bytes long, and therefore fit inside a 64-bit register. So it probably isn’t worth dealing with the portability issues of working with wide vector registers (Especially since I could not find a quick way to load an arbitrary number of bytes into a vector register with AVX2 nor with NEON.)&amp;quot;</text></comment>