5 ms·
FWIW, I learned a little bit about how to check for supported features in x86_64 from the linked code (I added the comments): TEXT ·hasPOPCNT(SB),NOSPLIT,$
by waynecochran 6y ago
FWIW, I learned a little bit about how to check for supported features in x86_64 from the linked code (I added the comments):
TEXT ·hasPOPCNT(SB),NOSPLIT,$0
XORQ AX, AX ; AX <- 0
INCL AX ; AX <- 1
CPUID ; check cpu caps
SHRQ $23, CX ; shift bit 23 to bit 0 (bit 23 => POPCNT support)
ANDQ $1, CX ; examine bit 0
MOVB CX, ret+0(FP) ; return 1 iff POPCNT instruction supported
RET
It seems Intel CPU's have such a bloated and varied instruction set you have to query to see what flavor of CPU you have to create portable code. Writing compilers for this must be a real pain.
Full disclosure : I work for Intel
- saagarjha 6y agoUsually what a compiler will do is target some "reasonable" CPU from the past, for your choice of "reasonable". For example, the C compiler on my system defaults to Penryn. Unrelated: what does Intel Sports do?
- waynecochran 6y agoImmersive viewing of sporting events: https://www.intel.com/content/www/us/en/sports/sports-overview.html https://www.intel.com/content/www/us/en/sports/sports-overvi...
- prattmic 6y agoWhile CPUID itself is far from elegant, I don't think ARM is fundamentally different. e.g., the ID_AA64ISAR1_EL1 register (ARM wins no naming awards here) indicates the presence of certain instructions. https://developer.arm.com/docs/ddi0595/h/aarch64-system-registers/id_aa64isar1_el1 https://developer.arm.com/docs/ddi0595/h/aarch64-system-regi...
- saagarjha 6y agoaarch64 is actually worse as far as I can tell because reading from that register is restricted and cannot be done from userspace (hence "EL1") and thus doing this portably is a huge pain. On Linux I think some of this is exported via /proc/cpuinfo and auxval, so that's what most people use… Edit: Also there's a ton of them, this lists a couple: https://www.kernel.org/doc/html/latest/arm64/cpu-feature-registers.html https://www.kernel.org/doc/html/latest/arm64/cpu-feature-reg...
- stephencanon 6y agoIt makes good sense for the OS to abstract this for a few reasons: - You'll get into trouble when migrating a process between cores in heterogeneous environments (having cores that support different ISA extensions is relatively uncommon today, but Intel has already announced some, and people have gotten into trouble in the past with e.g. reading cacheline sizes when big and little cores used different values). - This can be a pretty slow operation on some platforms, and the system can make it more efficient by caching it (and can provide additional capabilities information that may not have existed on older CPUs so that there's a uniform interface).
- saagarjha 6y agoI remember when that came up–I still don't understand how you are supposed to support a process that has queried the kernel for some feature ("can I use SVE2?"), gets an affirmative response, starts using that, and midway gets migrated to another core that doesn't support those instructions? Either you're going to either have the same extensions on all the cores, or lie about it by presenting the lowest common denominator to each of them…although I guess if you did want to do that, you'd want the kernel to be able to fake this from the start.
- loeg 6y ago> It seems Intel CPU's have such a bloated and varied instruction set you have to query to see what flavor of CPU you have to create portable code. Writing compilers for this must be a real pain. This is equally applicable to other x86 vendors. The alternatives are either (a) never introducing new instructions, or (b) making portable binaries impossible. Neither alternative seems better? It doesn't look quite as tortured in C: bool has_popcnt() { unsigned regs[4]; cpuid(1, regs); // You could name 23 CPUID_POPCOUNT_SHIFT or something. return ((regs[2] >> 23) & 1); } In normal C compilers (GCC, Clang) you can use ifuncs to do this resolution once at module load time, and then the correct function for the current CPU is dispatched cheaply afterwards.
- waynecochran 6y agoYes, adding features to a CPU is not a vendor specific problem. I wonder if this can be handled at the link / binary loader level so it is more transparent to the programmer. Almost like a patch using diff. You have a base binary that runs on any machine, but there are added diffs (added by the static linker) for different CPU versions and the OS loader patches and loads the code appropriately.
- loeg 6y agohttps://gcc.gnu.org/wiki/FunctionMultiVersioning https://gcc.gnu.org/wiki/FunctionMultiVersioning helps make this somewhat ergonomic, although it's still manual.
- Const-me 6y agoThese SIMD instructions are low-level micro-optimizations. Abstract an instruction behind a function call, and even for the very best case when all source data is already in correct registers and no need to save or restore anything, that indirect jump is likely to take the time comparable to the instructions being used there. One way to do it in C++, implement dispatching at higher level, where the function being dispatched takes much more time than a call overhead. Sometimes C++ templates or even C preprocessor can help reduce copy-pasting. Another way to do it is JIT from byte code. .NET tries to do that, I don’t think it’s too efficient currently, but I believe couple major versions in the future it might actually become good. It doesn’t have performance budget for expensive optimization modern C++ compilers do in optimized release builds. OTOH there’re other optimizations unavailable in C++: JIT may use whatever max AVX version is present, may compile immutable or rarely changing input data into code.
- RenThraysk 6y agoWhy does it use SHRQ, ANDQ & MOVB, instead of a BT & SETB ?
- waynecochran 6y agoGood question. The instruction set is so large and is not orthogonal and it difficult to know the "best way" to do things. I am always perplexed at the instruction selection compilers make, but the compiler writers are often pretty clever.