0
votes

It is known that a while-loop(Ex: while(n--)) or a for-loop(Ex: for(i=0; i<n; i++))'s time of execution depends on the variable n, i.e. O(n). Also, on an online judge, 10^7 operations ≈ 1s. But I tried executing a while-loop and a for-loop for n > 10^9 with few operations and it seems to run easily under 1 sec. I am curious why this is happening?

#include <iostream>
using namespace std;
#define ll long long 
int main(){
    ll t = 1e18;
    ll cnt = 0;
    while(t--){
        cnt++;
    }
    cout << cnt << '\n';
    cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << " s.\n";
}

Output: 1000000000000000000
Time elapsed: 0.003174 s.
3
The time complexity does not change and remains O(n). But the CPU may cache the instructions and so will execute them "bloody fast". Or the compiler has optimized the entire loop away and just assigned cnt= 1e18;Paul Ogilvie
Condition 10^7 operations ≈ 1s is never static and differs on different platformsthisisjaymehta

3 Answers

2
votes

The code you write is not instructions for your cpu, but instructions for your compiler to generate instructions for your cpu. In this specific case it is rather simple to see that this

long long  t = 1e18;
long long cnt = 0;
while(t--){
    cnt++;
}
cout << cnt << '\n';

can be replaced by

long long cnt = 1e18;
cout << cnt << '\n';

without altering the observable behavior of the program.

1
votes

TL;DR : The compiler simply discarded your loop , cleared t and stuck 1e18 into cnt. If you want to know how I came to this conclusion,read on:

On my computer and g++ -O1/-O0 the program took litraly forever to run but using -O2 it finished instantly (the same as the asker's situation), so I will use the -S -O2 assembly code:

leaq    _ZSt4cout(%rip), %rdi
    movabsq $1000000000000000000, %rsi
    movq    %fs:40, %rax
    movq    %rax, 8(%rsp)
    xorl    %eax, %eax
    call    _ZNSo9_M_insertIxEERSoT_@PLT
    leaq    7(%rsp), %rsi
    movl    $1, %edx
    movb    $10, 7(%rsp)
    movq    %rax, %rdi
    call    _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l@PLT

Clearly the compiler just stuck 1e18 into cnt (movabsq XXX %rsi), set t to 0 (using the magic of xorl %eax %eax to clear %eax) then skipped the loop (there was no jmp or je or similar commands,and proceeded to cout (via call _XXX_insert_XXX and later _ostream_insertXXX)

-2
votes

The time complexity for a simple loop (doesn't really matter which type) is O(n), that's clear.


But you should know that CPU's store some information in its fast access cache that is frequently used. However, the compiler optimizes the code by identifying which instructions (code) can be altered to produce lesser instructions while retaining the logic. While compiler optimization is a huge topic, loops are a prime target since they repeat the same set of instructions over and over.


And let's just consider the fact that a modern CPU executing a highly optimized set of compiled instructions at ~3 billion ops/sec will blaze through the instructions in microseconds.