4 ms·
Intrinsics tend to be incredibly difficult to read (and write) compared to hand-written assembly. For practical purposes, it's write-only code, and C simply do
by DarkShikari 15y ago
Intrinsics tend to be incredibly difficult to read (and write) compared to hand-written assembly. For practical purposes, it's write-only code, and C simply doesn't provide the syntactical niceties necessary to easily write assembly code.
Inlining SIMD functions is typically not possible in most applications unless you're compiling a dozen versions, one for each possible CPU, since different CPUs will use different SIMD functions that are optimized for their performance characteristics.
Nevermind the fact that intrinsics generally give atrocious performance compared to properly-written code, as your average 3-year-old can probably allocate registers better than gcc.
- psykotic 15y agoIntrinsics are about the same level of readability in my experience. Although it's annoying that compilers tend to pile on prefixes to the instruction mnemonics and thereby make everything longer. If that's a big problem for you, just use your own short-hand macro wrappers. > Inlining SIMD functions is typically not possible in most applications unless you're compiling a dozen versions, one for each possible CPU, since different CPUs will use different SIMD functions that are optimized for their performance characteristics. I mainly care about games when it comes to high-performance programming, so this is not an issue for me. Consoles are obviously fixed platforms, and on PC any game is likely to have high enough performance requirements across the board that you can compile your executable against a SSE3 min-spec target without excluding users because of that. > Nevermind the fact that intrinsics generally give atrocious performance, as your average 3-year-old can probably allocate registers better than gcc. I'm glad we agree that GCC is a pile of shit (and not just in this respect). MSVC's performance with intrinsics has also gone up and down with versions but by now it's pretty solid.
- DarkShikari 15y agoI mainly care about games when it comes to high-performance programming, so this is not an issue for me. Consoles are obviously fixed platforms, and on PC any game is likely to have high enough GPU requirements that you can compile your executable against a SSE3 min-spec target without excluding users. The instruction set available is often not the most relevant thing. In many cases, the optimal code will differ wildly between CPUs regardless of the available instruction sets. Let's look at some quick examples of performance characteristics: Athlon 64: Slow SSE2 unit, MMX is often faster than SSE2 for many functions. Phenom: Very fast SSE2 unit, but missing SSSE3 support, so can't take use the same optimized functions in many cases as the Core 2 and above. Significantly higher instruction latency than Core 2, so needs significantly more pipelining. Core 2 Conroe: SSE shuffle operations (punpck, etc) are excruciatingly slow: 4/2 for shuffles that use both arguments as input data (e.g. punpckldq), 2/2 for those that use only one argument (e.g. pshufb). Cacheline-split loads are extraordinarily painful (equivalent to an L1 cache miss, or ~14 cycles). Core 2 Penryn: Same as Conroe, but shuffles are fast now (1/1 for everything basically). Nehalem: Cacheline-split loads are cheap now (2 cycles), and shuffles have doubled throughput compared to Penryn (1/0.5). This doesn't even get into the more instruction-specific messiness, like how some CPUs like movddup and movhlps on integer data while others don't. It's not uncommon to have 3 or 4 assembly functions just to cover the latest CPUs -- not even counting old ones. Fortunately for our sanity, these are usually templated from a single function, with small changes to the relevant areas created via macros.
- psykotic 15y agoA gaming PC is already so fast compared to consoles that I'm generally not super concerned about those discrepancies. Compared to the big gains in going from no SIMD to SSE3-level SIMD, it's very minor. As I already said, if you have a few speed-critical inner loops (not generally the case in games but more the domain of programs like x264 and Bink 2) then go ahead and write the whole loop with a good macro assembler. But even then, Bink 2's encoder uses SSE in parts and is highly multi-threaded, and yet Jeff hasn't at all bothered with your level of per-CPU customization of the SSE code, and I doubt he ever will. Most programs aren't like codecs or even like games but could still make great gains from a properly administered dose of SIMD intrinsics.
- onan_barbarian 15y agoMy experience differs. We've used SIMD intrinsics extensively, and almost never use assembly for this. We write huge quantities of performance critical code, as do you. Many of the limitations that you discuss are real, but we've got strategies to avoid them. We do compile a dozen versions, which is horrible, but there it is. We get peak performance out of code using intrinsics. By 'peak', I mean close to the limits that can be achieved given the latency of the critical path and the number of operations that need to be carried out in total divided by the ports that can execute them. In order to persuade gcc to get register allocation and scheduling right, we typically have to program in a style that closely resembles assembly, and a fair bit of our critical code is automatically generated and hand-unrolled/pipelined/scheduled. This is still easier than coding everything in assembly, especially when something goes wrong. Large quantities of unrolling, and careful management of potential problems like aliasing, typically gets modern x86 what it needs to be able to overcome the problems of gcc. :-) Quite often the asm generated looks like shit but I have noticed a number of cases where improving the asm to be more sane actually reduces performance, often due to the fact that a modern x86 processor is doing weird things with the resulting code.