优化此汇编代码

时间:2012-01-26 19:15:36

标签: assembly computer-architecture

我现在正在参加计算机体系结构课程,我们正在讨论基本的R-type和I-type指令(另外,这是一个 RISC 架构)等等。我可以似乎没有弄清楚如何优化这段代码。

说明:此代码在数组中添加单词(由$ s1指向),直到达到零。结果存储在$ t1中。 $ t0保留当前的单词。

        add   $t1, $zero, $zero      # Initialize result to zero
again:  
        lw    $t0, 0($s1)            # Load the word from the array
        beq   $t0, $zero, done       # Terminate if current word is a zero
        add   $t1, $t1, $t0          # Add current word to result
        addi  $s1, $s1, 4            # Point to the next word in the array
        beq   $t1, $t1, again        # Loop again
done:
        nop                          # Do nothing

我很难优化代码。我觉得beq $t1, $t1, again(因为它总是如此)是不必要的,但我不知道如何删除它。这是我的尝试,但我现在意识到我的代码不会终止。

        add   $t1, $zero, $zero      # Initialize result to zero
again:  
        lw    $t0, 0($s1)            # Load the word from the array
        add   $t1, $t1, $t0          # Add current word to result
        addi  $s1, $s1, 4            # Point to the next word in the array
        bne   $t1, $zero, again      # If result is not zero, loop
done:
        nop                          # Do nothing

我从不检查终止零点并跳转完成。但是,如果我添加另一个检查,那么代码是不是和以前一样?

2 个答案:

答案 0 :(得分:2)

通常,您希望将顶部循环中的测试转换为底部循环中的测试。要做到这一点,你经常需要跳入(或多或少)循环体的中间进行第一次迭代。在伪代码中,你现在拥有的基本上是:

    initialize sum
beginning:
    load a word
    if (done) goto end
    add to sum
    increment pointer
    goto beginning
end:

为了优化它,我们希望将结构更改为:

    initialize sum
    goto start_loop

beginning:
    add to sum
    increment pointer
start_loop:
    load a word
    if (!done) goto beginning

这样每个循环只有一个跳转而不是两个跳转(它是一个短的向后跳转,所以目标几乎总是在缓存中)。

那就是说,我应该补充一点,这种优化实际上已经过时了 - 通过适当的分支预测,无条件跳转通常基本上是免费的。

编辑:由于已经提到了循环展开,我将加上我的两分钱。分支预测通常会使循环展开过时,除非您可以使用它来并行执行更多指令。这不是问题,但在现实生活中常常有用。例如,如果我们假设一个具有两个独立加法器的CPU,我们可以展开循环的两次迭代,并将这些迭代的结果添加到两个单独的目标寄存器中,因此我们通过在同一位添加两个值来利用这两个加法器时间。然后,当循环终止时,我们将这两个寄存器加在一起以获得最终值。在类似C的伪代码中,会出现类似这样的内容:

odds = 0
evens = 0

do {   
    evens += pointer[0];
    odds += pointer[1];
    pointer += 2;
while (pointer[0] && pointer[1]);
total = odds + evens;

如上所述,这增加了两个连续零的次要额外要求,以终止序列而不是一个,并且要添加的数组中至少有两个项目。但是请注意,并非真正的循环展开会带来主要优势,而是并行执行。

如果没有这一点,我们实际上只看到循环展开的好处,如果未采取的分支比采取的分支便宜(即使两者都被正确预测)。在较旧的处理器(例如,较旧的英特尔)上进行分支确实相对于未采用的分支进行了惩罚。同时,展开的循环将使用更多的缓存空间。因此,在现代处理器上,它经常是一个整体损失(除非,正如我之前所说,我们可以使用展开来获得并行性)。

答案 1 :(得分:1)

展开你的循环:


        add   $t1, $zero, $zero      # Initialize result to zero
again:  
        lw    $t0, 0($s1)            # Load the word from the array
        beq   $t0, $zero, done       # Terminate if current word is a zero
        add   $t1, $t1, $t0          # Add current word to result
        addi  $s1, $s1, 4            # Point to the next word in the array

        lw    $t0, 0($s1)            # Load the word from the array
        beq   $t0, $zero, done       # Terminate if current word is a zero
        add   $t1, $t1, $t0          # Add current word to result
        addi  $s1, $s1, 4            # Point to the next word in the array

        lw    $t0, 0($s1)            # Load the word from the array
        beq   $t0, $zero, done       # Terminate if current word is a zero
        add   $t1, $t1, $t0          # Add current word to result
        addi  $s1, $s1, 4            # Point to the next word in the array

        lw    $t0, 0($s1)            # Load the word from the array
        beq   $t0, $zero, done       # Terminate if current word is a zero
        add   $t1, $t1, $t0          # Add current word to result
        addi  $s1, $s1, 4            # Point to the next word in the array

        # and so on to a reasonable amount, 4-8 times are common.

        b     again        # Loop again
done:
        nop