因此,对于我在计算机系统课程中的最终作业,我们需要优化这些forloops,使其比原始版本更快.使用我们的linux服务器,基本等级不到7秒,完整等级不到5秒.我在这里的代码大约需要5.6秒.我想我可能需要以某种方式使用指针来使它更快,但我不是很确定.任何人都可以提供我的任何提示或选项吗?非常感谢!
QUICKEDIT:文件必须保持50行或更少,我忽略了教师所包含的那些注释行.
#include <stdio.h>
#include <stdlib.h>
// You are only allowed to make changes to this code as specified by the comments in it.
// The code you submit must have these two values.
#define N_TIMES 600000
#define ARRAY_SIZE 10000
int main(void)
{
double *array = calloc(ARRAY_SIZE, sizeof(double));
double sum = 0;
int i;
// You can add variables between this comment ...
register double sum1 = 0, sum2 = 0, sum3 = 0, sum4 = 0, sum5 = 0, …Run Code Online (Sandbox Code Playgroud) 给定N元素,仅处理第一个(0)和最后一个(N-1)元素.
但是,如果N = 1只处理单个元素一次.
使用一个或两次运行的循环,可以避免重复循环体.如果有一种可读的方法,那么它对源代码大小有好处.如果循环体很大,它也可能对机器代码大小有好处,并且编译器最终不会复制它.
我试过递增N-1但是当N=1(永远循环)时它不起作用.有没有技巧(反向循环fi)来解决这个问题?
for (i = 0 ; i < N ; i += (N - 1))
Run Code Online (Sandbox Code Playgroud)
我原来的问题涉及x,y,z方向的三个嵌套循环,这就是为什么我不能只处理elem [0])和elem [N-1].现在我有以下内容
#define forEachLglBound(i_,j_,k_) \
for(Int i_ = 0;i_ < NPX;i_+=((NPX>1) ? (NPX-1) : 1)) \
for(Int j_ = 0;j_ < NPY;j_+=((NPY>1) ? (NPY-1) : 1)) \
for(Int k_ = 0;k_ < NPZ;k_+=((NPZ>1) ? (NPZ-1) : 1))
Run Code Online (Sandbox Code Playgroud)
环境:
我没有写很多汇编程序代码,当我这么做时,它要么足够短,要么足够简单,以至于我不必担心压缩它的最大数量.我的更复杂的代码通常用C编写,我让编译器的优化器担心延迟,代码对齐等.
但是在我目前的项目中,MSVC的优化器在关键路径中的代码上做得非常糟糕.所以...
我还没有找到一个好的工具,可以对x64汇编代码进行静态或运行时分析,以便消除停顿,改善延迟等等.我所拥有的只是VS分析器,它告诉我(大致)哪些指令花了最多的时间.墙上的时钟告诉我最近的变化是否使事情变得更好或更糟.
作为替代方案,我一直在通过Agner的文档进行操作,希望能从我的代码中挤出一些更多的信息.问题是,在你理解了所有这些工作之前,很难理解他的任何工作.但它的一部分是有意义的,我正在尝试应用我学到的东西.
记住这一点,这里是我最内层循环的核心(不足为奇)是VS剖析器说我花费的时间:
nottop:
vpminub ymm2, ymm2, ymm3 ; reset out of range values
vpsubb ymm2, ymm2, ymm0 ; take a step
top:
vptest ymm2, ymm1 ; check for out of range values
jnz nottop
; Outer loop that does some math, does a "vpsubb ymm2, ymm2, ymm0",
; and eventually jumps back to top
Run Code Online (Sandbox Code Playgroud)
是的,这几乎是一个依赖链的教科书示例:这个紧密的小循环中的每个指令都取决于前一个操作的结果.这意味着没有并行性,这意味着我没有充分利用处理器.
受Agner的"优化汇编程序"文档的启发,我想出了一种方法(希望)允许我一次做2个操作,所以我可以有一个管道更新ymm2和另一个更新(比如说)ymm8.
虽然这是一个非平凡的变化,所以在我开始撕掉所有东西之前,我想知道它是否可能有所帮助.看看Agner的kaby lake(我的目标)的"指令表",我看到:
uops
each
port Latency
pminub …Run Code Online (Sandbox Code Playgroud) 从SIMD寄存器加载和存储生成目的寄存器的最佳方法是什么?到目前为止,我一直在使用堆栈作为临时.例如,
mov [rsp + 0x00], r8
mov [rsp + 0x08], r9
mov [rsp + 0x10], r10
mov [rsp + 0x18], r11
vmovdqa ymm0, [rsp] ; stack is properly aligned first.
Run Code Online (Sandbox Code Playgroud)
我认为没有任何指令可以直接(或另一个方向)执行此操作,因为它意味着具有五个操作数的指令.但是,上面的代码对我来说似乎很愚蠢.有没有更好的方法呢?我只能想到一个替代方案,使用pinsrd相关说明.但它似乎没有任何好转.
动机是,有时候在AVX2中做一些事情会更快,而其他用于通用的注册事项.例如,在一小段代码中,有四个64位无符号整数,我将需要四个xor,两个mulx来自BMI2.这将是更快的做xor用vpxor,但是,mulx没有一个AVX2等同.由于包装和拆包过程,任何vpxor对比4 的增益xor都会丢失.
为了有效地做x = x*10 + 1,它可能是最佳使用
lea eax, [rax + rax*4] ; x*=5
lea eax, [1 + rax*2] ; x = x*2 + 1
Run Code Online (Sandbox Code Playgroud)
3组件LEA在现代Intel CPU上具有更高的延迟,例如3个周期而不是Sandybridge系列的1个,因此disp32 + index*2比disp8 + base + index*1SnB系列更快,即我们关心优化的大多数主流x86 CPU.(这主要仅适用于LEA,而不适用于加载/存储,因为LEA运行在ALU执行单元上,而不是大多数现代x86 CPU中的AGU.)AMD CPU具有3个组件的LEA较慢或scale > 1(http://agner.org/optimize /)
但NASM和YASM将通过使用[1 + rax + rax*1]第二个LEA 来优化代码大小,第二个LEA只需要disp8而不是disp32.(寻址模式始终具有基址寄存器或disp32).
即他们总是分裂reg*2成base+index,因为对于代码大小来说,这永远不会更糟.
我可以强制使用disp32 lea eax, [dword 1 + rax*2],但这并不能阻止NASM或YASM分割寻址模式.在NASM手动似乎并没有记录的方式来使用的strict关键字上规模的因素,并[1 + …
我想知道是否有办法在不使用MUL或DIV指令的情况下执行任何乘法或除法,因为它们需要大量的CPU周期.我可以为此目标利用SHL或SHR指令吗?如何实现汇编代码?
为什么idivx86汇编指令EDX:EAX被给定寄存器除(64位)而其他数学运算(包括乘法)只能在单个输入和输出寄存器上运行?
乘法:
mov eax, 3
imul eax, 5
Run Code Online (Sandbox Code Playgroud)
师:
mov edx, 0
mov eax, 15
mov ebx, 5
idiv ebx
Run Code Online (Sandbox Code Playgroud)
我知道这EDX用于存储余数,但为什么没有单独的指令用于此行为?这对我来说似乎不一致.
我正在尝试做一些代码优化来消除分支,原始的c代码是
if( a < b )
k = (k<<1) + 1;
else
k = (k<<1)
Run Code Online (Sandbox Code Playgroud)
我打算用下面的汇编代码替换它
mov a, %rax
mov b, %rbx
mov k, %rcx
xor %rdx %rdx
shl 1, %rcx
cmp %rax, %rax
setb %rdx
add %rdx,%rcx
mov %rcx, k
Run Code Online (Sandbox Code Playgroud)
所以我写了内联汇编代码,如打击,
#define next(a, b, k)\
__asm__("shl $0x1, %0; \
xor %%rbx, %%rbx; \
cmp %1, %2; \
setb %%rbx; \
addl %%rbx,%0;":"+c"(k) :"g"(a),"g"(b))
Run Code Online (Sandbox Code Playgroud)
当我编译下面的代码时,我得到了错误:
operand type mismatch for `add'
operand type mismatch for `setb'
Run Code Online (Sandbox Code Playgroud)
我该如何解决?
我有一些在Release版本中编译的未知C++代码,因此它已经过优化.我正在努力的一点是:
xor al, al
add esp, 8
cmp byte ptr [ebp+userinput], 31h
movzx eax, al
Run Code Online (Sandbox Code Playgroud)
这是我的理解:
xor al, al ; set eax to 0x??????00 (clear last byte)
add esp, 8 ; for some unclear reason, set the stack pointer higher
cmp byte ptr [ebp+userinput], 31h ; set zero flag if user input was "1"
movzx eax, al ; set eax to AL and extend with zeros, so eax = 0x000000??
Run Code Online (Sandbox Code Playgroud)
我不关心第2行和第3行.由于流水线的原因,它们可能按此顺序存在,而恕我直言与EAX无关.
但是,我不明白为什么我会首先清除AL,以便稍后清除EAX的其余部分.结果将恕我直言EAX = 0,所以这也可能
xor eax, eax
Run Code Online (Sandbox Code Playgroud)
代替.这段代码的优势或"优化"是什么? …
我想使用链接汇编方法而不是C中的内联汇编方法将值从C程序传递到汇编.下面是正在处理的汇编程序(GCD).
;gcdasm.nasm
bits 64
section .text
global gcdasm
gcdasm:
push rbp
mov rbp, rsp
mov rax, [rbp+4] ;load rax with x
mov rbx, [rbp+8] ;load rbx with y
top:
cmp rax, rbx ;x(rax) has to be larger than y(rbx)
je exit ;if x=y then exit and return value y
jb xchange ;if x<y then swap x and y
modulo:
cqo ;RDX:RAX sign extend
div rbx ;div rdx:rax with rbx
cmp rdx, 0 ;check remider if its 0
je exit ;if …Run Code Online (Sandbox Code Playgroud) assembly ×8
x86 ×5
c ×4
avx2 ×2
loops ×2
nasm ×2
performance ×2
x86-64 ×2
c++ ×1
cpu-usage ×1
debug-mode ×1
division ×1
for-loop ×1
linux ×1
machine-code ×1
optimization ×1
simd ×1
sse2 ×1