0

My first submission of 61.Rotate List ran for 16 ms and I was not happy with that. So I changed this part of my code

k += 1;
while (--k) {
    p = p->next;
}

to

while (k) {
    p = p->next;
    --k;
}

and then magic happened. Runtime decreased to 8 ms.

So what's the difference between them? Why the runtime gap is so large?

Community
  • 1
  • 1
Liyin Shao
  • 25
  • 4

1 Answers1

3

This is probably just a compiler quirk or a benchmarking error. Code segments that produce identical results under all circumstances should theoretically compile to the same assembly. Usually the compiler fails to optimize if parts of the code are obfuscated (eg in different translation units) or if the code segment is complex enough that the optimizer fails to see the equivalency.

In this particular case, there should be no problem. Indeed, GCC compiles these segments to the same assembly.

struct P {
    P* next;
};

P* func1(unsigned int k, P* p) {
    k += 1;
    while (--k) {
        p = p->next;
    }
    return p;
}

P* func2(unsigned int k, P* p) {
    while (k) {
        p = p->next;
        --k;
    }
    return p;
}

The assembly output is

func1(unsigned int, P*):
movq %rsi, %rax
testl %edi, %edi
je .L2
.L3:
movq (%rax), %rax
subl $1, %edi
jne .L3
.L2:
ret
func2(unsigned int, P*):
movq %rsi, %rax
testl %edi, %edi
je .L10
.L11:
movq (%rax), %rax
subl $1, %edi
jne .L11
.L10:
ret

Jump labels aside, the assembly for those functions is identical. You can view it in godbolt here.

patatahooligan
  • 3,111
  • 1
  • 18
  • 27