Why is observed clock rate < 3MHz on Arduino Uno?
As Michael says there is a lot more going on than you think.

Firstly there are interrupts triggering which can slow things down (used to calculate milllis()).

Secondly you vastly under estimate the number of instructions that are being called.  For a start the loop is being called from a for(;;) loop in main():

    for (;;) {
        loop();
4   572:    0e 94 73 00     call    0xe6    ; 0xe6 &lt;loop&gt;
        if (serialEventRun) serialEventRun();
2   576:    20 97           sbiw    r28, 0x00   ; 0
2   578:    e1 f3           breq    .-8         ; 0x572 &lt;main+0x10&gt;
-   57a:    0e 94 1d 01     call    0x23a   ; 0x23a &lt;_Z14serialEventRunv&gt;
2   57e:    f9 cf           rjmp    .-14        ; 0x572 &lt;main+0x10&gt;
10 total


Then for the call to loop() the registers used get pushed to the stack, then your code executed, and the registers popped off again before returning.  This is the code for loop when disassembled:

000000e6 &lt;loop&gt;:
    // Save the registers to the stack
2   e6:     cf 92           push    r12
2   e8:     df 92           push    r13
2   ea:     ef 92           push    r14
2   ec:     ff 92           push    r15
8 total

    // Add to the sum
2   ee:     40 91 44 01     lds r20, 0x0144 ; 0x800144 &lt;counter&gt;
2   f2:     50 91 45 01     lds r21, 0x0145 ; 0x800145 &lt;counter+0x1&gt;
2   f6:     60 91 46 01     lds r22, 0x0146 ; 0x800146 &lt;counter+0x2&gt;
2   fa:     70 91 47 01     lds r23, 0x0147 ; 0x800147 &lt;counter+0x3&gt;
2   fe:     80 91 42 01     lds r24, 0x0142 ; 0x800142 &lt;sum&gt;
2   102:    90 91 43 01     lds r25, 0x0143 ; 0x800143 &lt;sum+0x1&gt;
1   106:    84 0f           add r24, r20
1   108:    95 1f           adc r25, r21
2   10a:    90 93 43 01     sts 0x0143, r25 ; 0x800143 &lt;sum+0x1&gt;
2   10e:    80 93 42 01     sts 0x0142, r24 ; 0x800142 &lt;sum&gt;
18 total

    // Check the value to see if it's time to print
1   112:    40 31           cpi r20, 0x10   ; 16
1   114:    57 42           sbci    r21, 0x27   ; 39
1   116:    61 05           cpc r22, r1
1   118:    71 05           cpc r23, r1
2   11a:    d1 f5           brne    .+116       ; 0x190 &lt;loop+0xaa&gt;
6 total

    // From here on is the "IF" section - skipped by the BRNE above
-   11c:    0e 94 f5 04     call    0x9ea   ; 0x9ea &lt;micros&gt;
-   120:    c0 90 3e 01     lds r12, 0x013E ; 0x80013e &lt;__data_end&gt;
-   124:    d0 90 3f 01     lds r13, 0x013F ; 0x80013f &lt;__data_end+0x1&gt;
-   128:    e0 90 40 01     lds r14, 0x0140 ; 0x800140 &lt;__data_end+0x2&gt;
-   12c:    f0 90 41 01     lds r15, 0x0141 ; 0x800141 &lt;__data_end+0x3&gt;
-   130:    6c 19           sub r22, r12
-   132:    7d 09           sbc r23, r13
-   134:    8e 09           sbc r24, r14
-   136:    9f 09           sbc r25, r15
-   138:    0e 94 68 06     call    0xcd0   ; 0xcd0 &lt;__floatunsisf&gt;
-   13c:    6b 01           movw    r12, r22
-   13e:    7c 01           movw    r14, r24
-   140:    60 91 44 01     lds r22, 0x0144 ; 0x800144 &lt;counter&gt;
-   144:    70 91 45 01     lds r23, 0x0145 ; 0x800145 &lt;counter+0x1&gt;
-   148:    80 91 46 01     lds r24, 0x0146 ; 0x800146 &lt;counter+0x2&gt;
-   14c:    90 91 47 01     lds r25, 0x0147 ; 0x800147 &lt;counter+0x3&gt;
-   150:    0e 94 6a 06     call    0xcd4   ; 0xcd4 &lt;__floatsisf&gt;
-   154:    9b 01           movw    r18, r22
-   156:    ac 01           movw    r20, r24
-   158:    c7 01           movw    r24, r14
-   15a:    b6 01           movw    r22, r12
-   15c:    0e 94 c7 05     call    0xb8e   ; 0xb8e &lt;__divsf3&gt;
-   160:    ab 01           movw    r20, r22
-   162:    bc 01           movw    r22, r24
-   164:    22 e0           ldi r18, 0x02   ; 2
-   166:    30 e0           ldi r19, 0x00   ; 0
-   168:    88 e4           ldi r24, 0x48   ; 72
-   16a:    91 e0           ldi r25, 0x01   ; 1
-   16c:    0e 94 a9 04     call    0x952   ; 0x952 &lt;_ZN5Print5printEdi&gt;
-   170:    60 e0           ldi r22, 0x00   ; 0
-   172:    71 e0           ldi r23, 0x01   ; 1
-   174:    88 e4           ldi r24, 0x48   ; 72
-   176:    91 e0           ldi r25, 0x01   ; 1
-   178:    0e 94 0b 03     call    0x616   ; 0x616 &lt;_ZN5Print7printlnEPKc&gt;
-   17c:    60 91 42 01     lds r22, 0x0142 ; 0x800142 &lt;sum&gt;
-   180:    70 91 43 01     lds r23, 0x0143 ; 0x800143 &lt;sum+0x1&gt;
-   184:    4a e0           ldi r20, 0x0A   ; 10
-   186:    50 e0           ldi r21, 0x00   ; 0
-   188:    88 e4           ldi r24, 0x48   ; 72
-   18a:    91 e0           ldi r25, 0x01   ; 1
-   18c:    0e 94 b0 03     call    0x760   ; 0x760 &lt;_ZN5Print7printlnEii&gt;

 // All this is just counter++
2   190:    80 91 44 01     lds r24, 0x0144 ; 0x800144 &lt;counter&gt;
2   194:    90 91 45 01     lds r25, 0x0145 ; 0x800145 &lt;counter+0x1&gt;
2   198:    a0 91 46 01     lds r26, 0x0146 ; 0x800146 &lt;counter+0x2&gt;
2   19c:    b0 91 47 01     lds r27, 0x0147 ; 0x800147 &lt;counter+0x3&gt;
2   1a0:    01 96           adiw    r24, 0x01   ; 1
1   1a2:    a1 1d           adc r26, r1
1   1a4:    b1 1d           adc r27, r1
2   1a6:    80 93 44 01     sts 0x0144, r24 ; 0x800144 &lt;counter&gt;
2   1aa:    90 93 45 01     sts 0x0145, r25 ; 0x800145 &lt;counter+0x1&gt;
2   1ae:    a0 93 46 01     sts 0x0146, r26 ; 0x800146 &lt;counter+0x2&gt;
2   1b2:    b0 93 47 01     sts 0x0147, r27 ; 0x800147 &lt;counter+0x3&gt;
20 total 

 // Get the registers back and return
2   1b6:    ff 90           pop r15
2   1b8:    ef 90           pop r14
2   1ba:    df 90           pop r13
2   1bc:    cf 90           pop r12
4   1be:    08 95           ret
12 total 


That's more than 10 instructions.  I make it 74 clocks to run one full iteration. (counter++ alone is 20 clock cycles - double your estimation for the total...)

You have to remember two important things:


AVR is a RISC CPU - that means that seemingly simple operations can take multiple instructions to perform.
AVR is an 8-bit CPU - that means that working with any variables bigger than 8 bits in size require far more complex code, and you work with 32-bit variables (and floats, but that's outside the timing loop section of your code).