【发布时间】:2021-12-04 03:58:24
【问题描述】:
我正在尝试手动对从数组中读取元素所花费的时间进行基准测试,因此我创建了一个从传递的数组中读取的汇编代码。
示例汇编代码:
; void array_read(long n, double *A);
; n must be divisible by 64
section .text
%ifidn __OUTPUT_FORMAT__, macho64
%define array_read _array_read
%endif
global array_read
array_read:
; rdi = n
; rsi = A
; rax = i
push rbx
mov rax, 0
align 16
.main_loop:
vmovaps ymm0, [rsi+8*(rax+ 0)]
vmovaps ymm1, [rsi+8*(rax+ 4)]
vmovaps ymm2, [rsi+8*(rax+ 8)]
vmovaps ymm3, [rsi+8*(rax+12)]
add rax, 64
cmp rax, rdi
jl .main_loop
.epilog:
; Restore caller registers
pop rbx
ret
我很幸运在 MacOS(Intel) 中它可以正确对齐并提供所需的输出,但在 Linux 中它提供了
Program received signal SIGSEGV: Segmentation fault - invalid memory reference.
Backtrace for this error:
#0 0x7fe37cdee20f in ???
#1 0x557940d91600 in ???
Segmentation fault (core dumped)
为什么会出现分段错误?
【问题讨论】:
-
您是在问如何首先在调用者中分配对齐的缓冲区吗?如果是这样,它甚至是用asm写的吗?如果没有,How to solve the 32-byte-alignment issue for AVX load/store operations?。或者您是否在询问处理数组的第一个可能未对齐的向量然后进行对齐加载的实现策略? (如果调用者传递一个未对齐的缓冲区,而不是仅仅使用
vmovups让硬件处理缓存行拆分的次要性能成本) -
我认为在调用组装方法时需要传递对齐的数据。这比试图在这里对齐要好。
-
另外,这里 RBX 的 push/pop 有什么意义?您的循环不需要任何保留调用的寄存器。它也不需要索引;它可以只增加指针。
-
array A存储了随机值,方法是将A传递给 Fortran 中的random_number函数。 -
为什么会出现段错误?因为您将未对齐的指针传递给使用
vmovaps的函数,该函数需要 32 字节对齐。阅读vmovaps(felixcloutier.com/x86/movaps) 与vmovups的手册。如果您想了解 Fortran 内存对齐规则,以及如何要求更多对齐,请改为询问。在 x86-64 System V ABI 中,alignof(max_align_t)是 16,因此 malloc 之类的东西只能保证返回具有那么多对齐的内存。 (也适用于全局数组)。
标签: assembly nasm memory-alignment avx