重用变量对于指令级并行和OoO执行是否有害?

时间:2018-04-25 16:11:53

标签: parallel-processing processor instructions

我正在研究处理器,引起我注意的一件事是,高性能CPU能够execute more than one instruction during a clock cycle甚至execute them out of order以提高性能。所有这些都没有编译器的任何帮助。

据我所知,处理器能够通过分析data dependencies来确定哪些指令可以先运行/在同一个ILP-paralell-step(问题)中执行。

@edit

我试着举个例子。想象一下这两段代码:

int myResult;

myResult = myFunc1(); // 1
myResult = myFunc2(); // 2
j = myResult + 3;     // 3

-

int myFirstResult, mySecondResult;

myFirstResult = myFunc1();  // 1
mySecondResult = myFunc2(); // 2
j = mySecondResult + 3;     // 3

他们都做同样的事情,区别在于,第一次我重用我的变量而在第二次我不做。

我假设(如果我错了,请纠正我)处理器可以在第二个例子的指令1之前运行指令2和3,因为数据将存储在两个不同的位置(寄存器?)。

对于第一个示例,这是不可能的,因为如果它在指令1之前运行指令2和3,则指令1上分配的值将保留在内存中(而不是指令2中的值)。

问题

如果我重用变量(如第一个例子中那样),是否有任何策略在1之前运行指令2和3?

或重用变量会阻止指令级并行和OoO执行吗?

1 个答案:

答案 0 :(得分:1)

现代微处理器是一种非常复杂的设备,并且已经具有足够的复杂性,因此了解其功能的每个方面都超出了大多数人的能力范围。您的编译器或运行时引入了一个额外的层,这增加了复杂性。这里的通用性真的很可能,因为ARM处理器X可能会处理这个问题而不是ARM处理器Y,这两者与Intel U或AMD V不同。

仔细查看您的代码:

int myResult;

myResult = myFunc1(); // 1
myResult = myFunc2(); // 2
j = myResult + 3;     // 3

int myResult行不一定按CPU执行任何操作。它只是指示编译器是名为myResult的{​​{1}}类型的变量。它没有初始化,所以没有必要做任何事情。

在第一次分配时,不使用该值。默认情况下,编译器通常会将代码直接转换为机器指令,但是当您启用通常用于生产代码的优化时,该假设就会消失。一个好的编译器会认识到这个值从未被使用过,并且会省略赋值。一个更好的编译器会警告你,该值永远不会被使用。

第二个实际上分配给变量该变量稍后使用。显然,在第三次任务发生之前,必须完成第二项任务。除非这些功能微不足道并最终内联,否则这里的优化并不多。那么这就是这些功能的作用。

“超标量”处理器,或能够无序运行的处理器,限制了它可以获得的雄心壮志。它最适合的代码类型类似于以下内容:

int

int a = 1; int b = f(); int c = a * 2; int d = a + 2; int e = g(b); 的分配很简单直接。 a是计算值。令人感兴趣的地方是bc具有相同的依赖关系,并且实际上可以并行执行。他们也不依赖于d所以理论上他们可以在b调用之前,期间或之后运行,只要结束状态是正确的。

单个线程可以同时执行多个操作,但大多数处理器对它们的类​​型和数量有限制。例如,可能发生浮点乘法和整数加,或两个整数加,但不是两个浮点乘法运算。这取决于CPU具有哪些操作,可以操作哪些寄存器,以及编译器如何提前排列数据。

如果您希望优化代码并减少纳秒数,您需要在目标CPU上找到一本非常好的技术手册,并花费数小时尝试不同的方法和基准测试。

简短的回答是变量无关紧要。这完全取决于依赖项,编译器以及CPU的功能。