相关疑难解决方法(0)

x64 nasm:将内存地址推送到堆栈和调用函数

我对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)

macos assembly gcc x86-64 nasm

5
推荐指数
2
解决办法
5882
查看次数

x86_64 CPU 能否在流水线的同一阶段执行两个相同的操作?

众所周知,Intel x86_64 处理器不仅是流水线架构,而且还是超标量架构。

这意味着 CPU 可以:

  1. 流水线- 在一个时钟下,执行一个操作的某些阶段。例如,与阶段转移并行的两个 ADD:

    • ADD(stage1) -> ADD(stage2) -> 没有
    • 什么都没有 -> ADD(stage1) -> ADD(stage2)
  2. 超标量- 在一个时钟上,执行一些不同的操作。例如,在同一阶段并行 ADD 和 MUL:

    • 添加(阶段 1)-> 添加(阶段 2)
    • MUL(stage1) -> MUL(stage2)

在此处输入图片说明

这是可能的,因为处理器有多个指令调度程序(英特尔酷睿有 4 个简单解码器)。

但是只有调度程序(4个简单解码器)的副本,还是算术单元的副本?

例如,我们可以在相同的阶段执行两个 ADD,但是在同一个 CPU 内核上的独立算术单元(例如,端口 0 上的 ALU 和端口 1上的ALU)?

  • ADD1(stage1) -> ADD1(stage2)
  • ADD2(stage1) -> ADD2(stage2)

是否有任何执行单元的副本,可以在同一个时钟执行两条相同的指令?

cpu x86 x86-64 intel cpu-architecture

5
推荐指数
1
解决办法
1239
查看次数

代码对齐在组装中定时主循环的影响

假设我有以下主循环

.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%).我不清楚如何选择代码对齐方式.我可以想到三个地方,我可能想要对齐代码

  1. 在函数入口处(参见triad_fma_asm_repeat下面的代码中)
  2. 在外循环的开始(.L1上面)重复我的主循环
  3. 在我的主循环开始时(.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)

x86 assembly nasm yasm

5
推荐指数
1
解决办法
576
查看次数

为什么带有内存操作数的 vpclmulqdq 比 movdqa + pclmulqdq 慢?

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)

x86 assembly sse avx micro-optimization

5
推荐指数
0
解决办法
467
查看次数

存储转发地址与数据:英特尔优化指南中的STD和STA有什么区别?

我想知道是否有任何英特尔专家可以告诉我STD和STA在英特尔Skylake内核方面的区别。

在英特尔优化指南中,有一张图片描述了英特尔酷睿的“超标量端口”。

这是PDF。图片在第40页上。

这是相关图形的图片

这是第78页的另一张图片,该图片描述了“存储地址”和“存储数据”:

  1. 使用存储的数据地址准备存储转发和存储退出逻辑。

  2. 准备存储转发和存储退出逻辑以及要存储的数据。

考虑到Skylake可以在每个时钟周期执行一次#1 3x,但是在每个时钟周期只能执行一次#2,我很好奇这两者之间的区别。

在我看来,将存储转发到数据地址是“自然的”。但是我无法理解何时进行数据存储转发(又名:STD /端口4)。是否有任何组装/优化专家可以帮助我准确了解STD和STA之间的区别?

optimization performance assembly intel cpu-architecture

5
推荐指数
1
解决办法
697
查看次数

超标量和VLIW

我想问一些与ILP有关的问题.

  • 超标量处理器是标量和矢量处理器的混合体.那么我可以说矢量处理器的架构遵循超标量吗?

  • 同时处理多个指令并不能使架构超标量化,因为流水线,多处理器或多核架构也实现了这一点.这意味着什么?

  • 我读过'超标量CPU架构在单个处理器中实现了一种称为指令级并行的并行形式',超标量不能使用多个处理器?任何人都可以提供我使用超标量的例子吗?

  • VLIW,我已经完成了本文第9页的图4.它显示了一个通用的VLIW实现,没有复杂的重排序缓冲区和解码和调度逻辑.没有解码的术语令我感到困惑.

此致,anas anjaria

parallel-processing cross-platform cpu-architecture vliw

4
推荐指数
1
解决办法
2290
查看次数

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

我有一个内存位置,其中包含一个我想要与另一个角色进行比较的角色(并且它不在堆栈的顶部,所以我不能只是pop它).如何引用内存位置的内容以便进行比较?

基本上我如何在语法上做到这一点.

x86 assembly masm addressing-mode

4
推荐指数
1
解决办法
6687
查看次数

增加指针比执行"mov [pointer + 1],eax"更快吗?

假设我们想在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)

x86 assembly

4
推荐指数
1
解决办法
752
查看次数

在C++ SIMD中将signed short转换为float

我有一个带符号的短数组,我希望除以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++ sse simd avx2

4
推荐指数
2
解决办法
1245
查看次数

对于启用优化的大型数组,内联汇编数组总和基准时间接近于零,即使使用结果

我编写了两个获取数组总和的函数,第一个是用 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)

c++ optimization performance gcc inline-assembly

4
推荐指数
1
解决办法
243
查看次数