I have heard that it is more efficient, in C based languages, to pre-increment (++counter) than it is to post-increment (counter++). However all the online discusions of the topic that I have seen did not provide actual evidence as to why that might be. One of the things I have learned about technology is that there are very few rules of thumb that last, especially when it comes to limitations. So, I decided to run a couple small tests using my current language of choice, Objective-C.

Let’s start with the most basic example. We will define an integer, post-increment it, pre-increment it, and then look at the generated asembly code. I entered the following Objective-C code into Xcode. (By the way, using int instead of NSInteger will produce the same results, but Apple recomends using NSInteger since it is a 32-bit int on 32-bit platforms and a 64-bit int on 64-bit platforms)

NSInteger i = 0;
i++;
++i;

The C code resulted in the following LLVM assembly code.

movl	$0, -20(%ebp)

movl	-20(%ebp), %edx
addl	$1, %edx
movl	%edx, -20(%ebp)

movl	-20(%ebp), %edx
addl	$1, %edx
movl	%edx, -20(%ebp)

Based upon this very simple example it seems that there is no difference, and that makes sense since they are both unary operators in this example. This code might seems too trivial, but it is used all the time in cases like this: for (int i = 0; i < 100; ++i) {} (testing the loop condition and incrementing the loop counter are discrete steps, so pre- or post-incrementing does not matter in that regard).

What if we make the code a little more complicated, will that prevent the compiler from optimizing the resulting assembly code?

NSInteger i = 0;

NSInteger j = 0;
j = ++i;   // i = i + 1; j = i;

NSInteger k = 0;
k = i++;   // k = i; i = i + 1;

The above Objective-C code is compiled into the following LLVM assembly code.

movl	$0, -20(%ebp)


movl	$0, -24(%ebp)

movl	-20(%ebp), %edx
addl	$1, %edx
movl	%edx, -20(%ebp)
movl	%edx, -24(%ebp)


movl	$0, -28(%ebp)

movl	-20(%ebp), %edx
movl	%edx, %esi
addl	$1, %esi
movl	%esi, -20(%ebp)
movl	%edx, -28(%ebp)

OK, now we are seeing that extra code they must have been talking about (movl %edx, %esi), or are we? Even though we have one extra line (20% more) of assembly code in the post-increment case, we are actually accomplishing a quite different task. In the first case we are setting both i and j to the value of i + 1; in the second case we are setting j to the value of i and then setting i to the value of i + 1. After this code runs the value of j is 1, but the value of k is also 1.

Furthermore, since both of these statements are so semantically similar and yet produce quite different results, most Computer Science students are taught not to make these types of increment and assignment statements. Any added code efficiency is not offset by the reduction in code clarity and increased risk of producing bug from an off by one error. So, it seems that this does not provide evidence of pre-increment being more efficient.

Assuming that what I just said about trading a small bit of code efficiency for readbility and fault prevention is true, then it would seem that we could choose to pre- or post- increment based upon which one provides more code clarity. So, which one to use? I don’t know, consult your style guide.

Tags: Objective-C C LLVM Assembly

Published: 2014-01-18