Tag: 汇编

用64位代替32位循环计数variables引入了疯狂的性能偏差

我正在寻找最快的方式来popcount大量的数据。 我遇到了一个非常奇怪的结果:将循环variables从unsigned更改为uint64_t使得我的PC上的性能下降了50%。 基准 #include <iostream> #include <chrono> #include <x86intrin.h> int main(int argc, char* argv[]) { using namespace std; if (argc != 2) { cerr << "usage: array_size in MB" << endl; return -1; } uint64_t size = atol(argv[1])<<20; uint64_t* buffer = new uint64_t[size/8]; char* charbuffer = reinterpret_cast<char*>(buffer); for (unsigned i=0; i<size; ++i) charbuffer[i] = rand()%256; […]

如果只需要结果的低部分,那么哪个2的补码整数运算可以在input中不调零高位的情况下使用?

在汇编编程中,通常需要从寄存器的低位计算某些值,而不保证其他位为零。 在像C这样的高级语言中,只需简单地将input转换为较小的大小,然后让编译器决定是否需要单独清零每个input的高位,或者是否可以在结束之后剔除结果的高位事实。 对于x86-64(也就是AMD64)来说,这是非常常见的,原因有很多,其中一些出现在其他ISA中。 我将用64位x86作为例子,但其目的是一般性地询问/讨论2的补码和无符号的二进制算术,因为所有现代的CPU都使用它 。 (请注意,C和C ++不保证二进制补码4 ,并且这个有符号溢出是未定义的行为。) 作为例子,考虑一个可以编译成LEA指令的简单函数2 。 (在x86-64 SysV(Linux) ABI 3中 ,前两个函数参数在rdi和rsi ,返回值是rax , int是32位types。 ; int intfunc(int a, int b) { return a + b*4 + 3; } intfunc: lea eax, [edi + esi*4 + 3] ; the obvious choice, but gcc can do better ret gcc知道,即使是负符号整数,除了从右到左,input的高位不会影响进入eax 。 因此, 它保存了一个指令字节并使用 lea […]

我怎样才能乘以和仅使用位移和添加?

我怎样才能乘以和仅使用位移和添加?

num ++可以是'int num'的primefaces吗?

通常,对于int num , num++ (或++num )作为读取 – 修改 – 写入操作是不primefaces的 。 但是我经常看到编译器,比如GCC ,为它生成下面的代码( 试试这里 ): 由于对应于num++第5行是一条指令,所以在这种情况下,我们可以得出结论: num++ 是primefaces吗? 如果是这样, 这是否意味着这样生成的num++可以在并发(multithreading)的情况下使用,没有任何数据竞争的危险 (即,我们不需要,例如, std::atomic<int>并强加相关的成本,因为它primefaces无论如何)? UPDATE 请注意,这个问题并不是增量是否是primefaces的(这不是,而且是问题的开场白线)。 这是否可以在特定情况下,即在某些情况下是否可以利用单指令性质来避免lock前缀的开销。 而且,正如接受的答案中提到的关于单处理机的部分,以及这个答案 ,在它的评论和其他人的谈话中解释, 它可以 (但不是用C或C ++)。

为什么你必须链接C中的math库?

如果在C程序中包含<stdlib.h>或<stdio.h> ,那么在编译时就不必将它们链接起来,但是我必须使用-lm和gcc链接到<math.h> ,例如: gcc test.c -o test -lm 这是什么原因? 为什么我必须明确地链接math库,而不是其他库?

为什么循环指令很慢? 英特尔能不能有效地实现它?

loop递减ecx / rcx,然后在非零时跳转 。 速度很慢,但英特尔不能以低廉的速度让它变得更快? 一个dec和分支uop已经是可能的了(唯一的区别是设置标志)。 从Agner Fog的指令表中 loop使用各种微架构: K8 / K10:7米 推土机家族/ Ryzen:1 m-op(与macros电子testing分支相同的成本,或jecxz ) P4:4个jecxz (与jecxz相同) P6(PII / PIII):8个 Pentium M,Core2:11 uops Nehalem:6个uops。 (11 loope / loopne ) SnB系列:7个。 (11个为loope / loopne )。 对于jecxz只有2个jecxz Silvermont:7个uops AMD捷豹(低功耗):8个微处理器,5c吞吐量 通过Nano3000:2微软 难道解码器只是解码像lea rcx, [rcx-1] / jrcxz ? 那将是3个微笑。 至less在没有地址大小的前缀的情况下是这样,否则如果跳转被使用,则必须使用ecx并截断RIP到EIP 。 也许地址大小控制的减less宽度的奇数select解释了许多微软。 或者更好,只是将它解码为一个融合的十进制分支,不设置标志? jnz上的dec ecx / jnz解码为单个uop(设置标志)。 我知道真正的代码并没有使用它(因为它至lessP5或者其他东西已经很慢了),但是AMD决定让推土机变得很快是值得的。 […]

引用内存位置的内容。 (x86寻址模式)

我有一个内存位置包含一个字符,我想与另一个字符比较(它不在堆栈的顶部,所以我不能只是pop它)。 我如何引用一个内存位置的内容,以便我可以比较它? 基本上,我怎样才能在语法上做到这一点。

为什么大部分的x64指令会将32位寄存器的上半部分清零?

今天我了解了x64程序集(资料来源: http : //x86asm.net/articles/x86-64-tour-of-intel-manuals/ ) 也许最令人惊讶的事实是,诸如MOV EAX,EBX之类的指令自动将RAX寄存器的高32位清零。 英特尔文档(3.4.1.1通用基本体系结构中的64位模式的通用寄存器)在同一来源中引用告诉我们: 64位操作数在目标通用寄存器中生成一个64位结果。 32位操作数产生一个32位结果,在目标通用寄存器中零扩展为一个64位结果。 8位和16位操作数生成8位或16位结果。 目标通用寄存器的高56位或48位(分别)不会被操作修改。 如果8位或16位操作的结果是用于64位地址计算的,则明确地将寄存器扩展为完整的64位。 在x86-32汇编中,16位指令如 mov ax, bx 不要performance出这种eax的上位字“零”的“奇怪”的行为。 因此:这种行为被引入的原因是什么? 乍一看这似乎不合逻辑(但原因可能是我习惯了x86-32汇编的怪癖)。

微融合和寻址模式

使用英特尔®架构代码分析器 (IACA),我发现了一些意想不到的情况(对我来说)。 以下指令使用[base+index]寻址 addps xmm1, xmmword ptr [rsi+rax*1] 根据IACA没有微熔丝。 但是,如果我这样使用[base+offset] addps xmm1, xmmword ptr [rsi] IACA报告说它确实融合了。 “ 英特尔优化参考手册”第2-11部分提供了以下内容作为“可由所有解码器处理的微型熔合微操作” FADD DOUBLE PTR [RDI + RSI*8] Agner Fog的优化组装手册也给出了使用[base+index]寻址的微操作融合的例子。 例如,请参见第12.2节“Core2上的相同示例”。 那么正确的答案是什么?

在i386和x86-64上,UNIX和Linux系统调用的调用约定是什么?

以下链接解释了UNIX(BSD flavor)和Linux的x86-32系统调用约定: http://www.int80h.org/bsdasm/#system-calls http://www.freebsd.org/doc/en/books/developers-handbook/x86-system-calls.html 但是,UNIX和Linux上的x86-64系统调用约定是什么?