Якщо натиснути "Open in Compiler Explorer" (перехід на Godbolt), то вилазить такий код для тестових циклів цих двох варіантів:
.LBB0_3: # =>This Inner Loop Header: Depth=1
add qword ptr [rsp + 8], 1
add rbx, -1
jne .LBB0_3
i
.LBB1_3: # =>This Inner Loop Header: Depth=1
lea rcx, [rax + 1]
mov rax, rcx
cmp rbx, rcx
jne .LBB1_3
Як бачите, в першому випадку компілятор наполіг на тому, щоб зберегти x як фізично існуючу змінну в пам'яті. А в другому випадку представив її регістром. Звідси й різниця в продуктивності. Я б припустив, що це наслідок того, що результат преінкремента в С++ є lvalue. І цей тестбенч з якоїсь причини наполягає на тому, щоб зберегти lvalue-ність результату. Швидше за все це навіть фіча, тобто документована властивість benchmark::DoNotOptimize, про яку потрібно пам'ятати.
Якщо в першому випадку "допомогти" компілятору зрозуміти, що нам нафіг не потрібна lvalue-ність результату ++x, наприклад за допомогою явного приведення типу
benchmark::DoNotOptimize(std::size_t{++x});
, то результати виходять набагато рівніші за продуктивністю.