我对Mac上的x64-assembly很新,所以我很困惑在64位中移植一些32位代码.
程序应该只通过printfC标准库中的函数打印出一条消息.
我已经开始使用这段代码:
section .data
msg db 'This is a test', 10, 0 ; something stupid here
section .text
global _main
extern _printf
_main:
push rbp
mov rbp, rsp
push msg
call _printf
mov rsp, rbp
pop rbp
ret
Run Code Online (Sandbox Code Playgroud)
用这种方式用nasm编译它:
$ nasm -f macho64 main.s
Run Code Online (Sandbox Code Playgroud)
返回以下错误:
main.s:12: error: Mach-O 64-bit format does not support 32-bit absolute addresses
Run Code Online (Sandbox Code Playgroud)
我试图修复问题字节,将代码更改为:
section .data
msg db 'This is a test', 10, 0 ; something stupid here
section .text
global _main
extern _printf …Run Code Online (Sandbox Code Playgroud) 众所周知,Intel x86_64 处理器不仅是流水线架构,而且还是超标量架构。
这意味着 CPU 可以:
流水线- 在一个时钟下,执行一个操作的某些阶段。例如,与阶段转移并行的两个 ADD:
超标量- 在一个时钟上,执行一些不同的操作。例如,在同一阶段并行 ADD 和 MUL:

这是可能的,因为处理器有多个指令调度程序(英特尔酷睿有 4 个简单解码器)。
但是只有调度程序(4个简单解码器)的副本,还是算术单元的副本?
例如,我们可以在相同的阶段执行两个 ADD,但是在同一个 CPU 内核上的独立算术单元(例如,端口 0 上的 ALU 和端口 1上的ALU)?
是否有任何执行单元的副本,可以在同一个时钟执行两条相同的指令?
假设我有以下主循环
.L2:
vmulps ymm1, ymm2, [rdi+rax]
vaddps ymm1, ymm1, [rsi+rax]
vmovaps [rdx+rax], ymm1
add rax, 32
jne .L2
Run Code Online (Sandbox Code Playgroud)
我想时间的方式是把它放在另一个像这样的长循环中
;align 32
.L1:
mov rax, rcx
neg rax
align 32
.L2:
vmulps ymm1, ymm2, [rdi+rax]
vaddps ymm1, ymm1, [rsi+rax]
vmovaps [rdx+rax], ymm1
add rax, 32
jne .L2
sub r8d, 1 ; r8 contains a large integer
jnz .L1
Run Code Online (Sandbox Code Playgroud)
我发现的是我选择的对齐方式会对时序产生重大影响(最高可达+ -10%).我不清楚如何选择代码对齐方式.我可以想到三个地方,我可能想要对齐代码
triad_fma_asm_repeat下面的代码中).L1上面)重复我的主循环.L2上图). 我发现的另一件事是,如果我在源文件中放入另一个例程,即更改一条指令(例如删除指令),即使它们是独立函数,也会对下一个函数的时序产生重大影响.我甚至在过去看到过影响另一个目标文件中的例程.
我在Agner Fog的优化装配手册中阅读了第11.5节"代码对齐",但我仍然不清楚调整代码以测试性能的最佳方法.他给出了一个例子,11.5,计时内循环,我并没有真正遵循.
目前,从我的代码中获得最高性能是一种猜测不同值和对齐位置的游戏.
我想知道是否有一种智能方法可以选择对齐方式?我应该对齐内圈和外圈吗?只是内循环?该功能的入口?使用短期或长期NOP是否重要?
我最感兴趣的是Haswell,其次是SNB/IVB,然后是Core2.
我尝试了NASM和YASM,并发现这是一个显着不同的领域.NASM仅插入一个字节的NOP指令,其中YASM插入多字节NOP.例如,通过将上面的内部和外部循环对齐到32字节,NASM插入20条NOP(0x90)指令,其中YASM插入以下内容(来自objdump)
2c: 66 …Run Code Online (Sandbox Code Playgroud) vpclmulqdq指令有四个操作数,pclmulqdq有三个操作数,所以我认为vpclmulqdq可以用 代替movdqa + pclmulqdq,但实验结果变慢了。
但是当我使用vpaddd而不是movdqa + paddd,我得到更快的结果。所以我对这个问题感到困惑。代码使用如下paddd指令:
movdqa %xmm0, %xmm8 # slower
movdqa %xmm0, %xmm9
movdqa %xmm0, %xmm10
movdqa %xmm0, %xmm11
paddd (ONE), %xmm8
paddd (TWO), %xmm9
paddd (THREE), %xmm10
paddd (FOUR), %xmm11
vpaddd (ONE), %xmm0, %xmm8 # faster
vpaddd (TWO), %xmm0, %xmm9
vpaddd (THREE), %xmm0, %xmm10
vpaddd (FOUR), %xmm0, %xmm11
Run Code Online (Sandbox Code Playgroud)
代码使用 pclmulqdq 指令,如:
movdqa %xmm15, %xmm1 # faster
pclmulqdq $0x00, (%rbp), %xmm1
aesenc 16(%r15), %xmm8
aesenc …Run Code Online (Sandbox Code Playgroud) 我想知道是否有任何英特尔专家可以告诉我STD和STA在英特尔Skylake内核方面的区别。
在英特尔优化指南中,有一张图片描述了英特尔酷睿的“超标量端口”。
这是PDF。图片在第40页上。
。
这是第78页的另一张图片,该图片描述了“存储地址”和“存储数据”:
使用存储的数据地址准备存储转发和存储退出逻辑。
准备存储转发和存储退出逻辑以及要存储的数据。
考虑到Skylake可以在每个时钟周期执行一次#1 3x,但是在每个时钟周期只能执行一次#2,我很好奇这两者之间的区别。
在我看来,将存储转发到数据地址是“自然的”。但是我无法理解何时进行数据存储转发(又名:STD /端口4)。是否有任何组装/优化专家可以帮助我准确了解STD和STA之间的区别?
我想问一些与ILP有关的问题.
超标量处理器是标量和矢量处理器的混合体.那么我可以说矢量处理器的架构遵循超标量吗?
同时处理多个指令并不能使架构超标量化,因为流水线,多处理器或多核架构也实现了这一点.这意味着什么?
我读过'超标量CPU架构在单个处理器中实现了一种称为指令级并行的并行形式',超标量不能使用多个处理器?任何人都可以提供我使用超标量的例子吗?
VLIW,我已经完成了本文第9页的图4.它显示了一个通用的VLIW实现,没有复杂的重排序缓冲区和解码和调度逻辑.没有解码的术语令我感到困惑.
此致,anas anjaria
我有一个内存位置,其中包含一个我想要与另一个角色进行比较的角色(并且它不在堆栈的顶部,所以我不能只是pop它).如何引用内存位置的内容以便进行比较?
基本上我如何在语法上做到这一点.
假设我们想在EDI存储一个字符串.以这种方式存储它会更快吗?
mov byte [edi],0
mov byte [edi+1],1
mov byte [edi+2],2
mov byte [edi+3],3
...
Run Code Online (Sandbox Code Playgroud)
还是这样?
mov byte [edi],0
inc edi
mov byte [edi],1
inc edi
mov byte [edi],2
inc edi
mov byte [edi],3
inc edi
...
Run Code Online (Sandbox Code Playgroud)
有些人可能会在小端看到以下内容:
mov dword [edi],0x3210
Run Code Online (Sandbox Code Playgroud)
或者以下是big-endian:
mov dword [edi],0x0123
Run Code Online (Sandbox Code Playgroud)
但这不是我的问题.我的问题是,增加指针然后执行mov需要更多指令是否更快,或者更快地在每个mov指令中指定添加到EDI指向的偏移地址的数量?如果后者为真,那么在将多少个具有相同数字的mov指令添加到偏移地址之后,是否值得将该数量添加到指针?换句话说,就是这样
mov byte [edi+5],0xFF
mov byte [edi+5],0xFF
mov byte [edi+5],0xFF
mov byte [edi+5],0xFF
Run Code Online (Sandbox Code Playgroud)
比这更快?
add edi,5
mov byte [edi],0xFF
mov byte [edi],0xFF
mov byte [edi],0xFF
mov byte [edi],0xFF
Run Code Online (Sandbox Code Playgroud) 我有一个带符号的短数组,我希望除以2048并得到一个浮点数组.
我发现SSE:将短整数转换为浮点数,允许将无符号短路转换为浮点数,但我也想处理签名短路.
下面的代码有效但仅适用于正面短路.
// We want to divide some signed short by 2048 and get a float.
const auto floatScale = _mm256_set1_ps(2048);
short* shortsInput = /* values from somewhere */;
float* floatsOutput = /* initialized */;
__m128i* m128iInput = (__m128i*)&shortsInput[0];
// Converts the short vectors to 2 float vectors. This works, but only for positive shorts.
__m128i m128iLow = _mm_unpacklo_epi16(m128iInput[0], _mm_setzero_si128());
__m128i m128iHigh = _mm_unpackhi_epi16(m128iInput[0], _mm_setzero_si128());
__m128 m128Low = _mm_cvtepi32_ps(m128iLow);
__m128 m128High = _mm_cvtepi32_ps(m128iHigh);
// Puts …Run Code Online (Sandbox Code Playgroud) 我编写了两个获取数组总和的函数,第一个是用 C++ 编写的,另一个是用内联汇编 (x86-64) 编写的,我比较了这两个函数在我的设备上的性能。
如果在编译期间未启用-O标志,则使用内联汇编的函数几乎比 C++ 版本快 4-5 倍。
cpp time : 543070068 nanoseconds
cpp time : 547990578 nanoseconds
asm time : 185495494 nanoseconds
asm time : 188597476 nanoseconds
Run Code Online (Sandbox Code Playgroud)
如果-O标志设置为-O1,它们会产生相同的性能。
cpp time : 177510914 nanoseconds
cpp time : 178084988 nanoseconds
asm time : 179036546 nanoseconds
asm time : 181641378 nanoseconds
Run Code Online (Sandbox Code Playgroud)
但是,如果我尝试将-O标志设置为-O2或-O3,则使用内联汇编编写的函数会得到不寻常的2-3 位纳秒性能,该性能速度很快(至少对我来说,请耐心等待,因为我对汇编编程没有扎实的经验,所以我不知道它与用 C++ 编写的程序相比有多快或多慢。)
cpp time : 177522894 nanoseconds
cpp time : 183816275 nanoseconds …Run Code Online (Sandbox Code Playgroud)