Khác biệt giữa i++ và ++i

Bởi vì các hàm gettime đều tương đối, nên mình với test 100 lần, mỗi lần lặp 50 triệu lần rồi mới lấy kết quả để giảm sai số.
Tuy nhiên cả 2 lần test đều cho kết quả ngang nhau.
Asm trên là đoạn asm cho cả một hàm xử lý dữ lieu bên trong 1 object nên nó mới nhiều mã asm như vậy.
Bản than I++ hay ++I cũng chỉ 4 asm lệnh thôi.

đo tốc độ xài std::chrono::stead_clock::now() ấy, xài GetTickCount làm gì

mà thật khó tin cái proc1 và proc2 ko bị optimized thành 1 dòng code? 1 vòng for với i chạy từ 0 tới 1 giá trị define cho trước, val chả làm gì trong 49.999999 triệu lần trước chỉ cần giá trị sau cùng, vậy mà ko optimize được?

à nó còn phải ++ thêm giá trị cho data, vậy chắc ko optimize được @_@ Chắc là do thời gian tương đối thôi =)

edit: chạy thử thấy đúng là val = data[i]++ nhanh hơn thật, nhưng khi để data[i]++ ko mà ko gán cho val thì lại xấp xỉ như nhau, có gì uẩn khúc ở đây, oan ức quá =)

đổi thành val += ... thì ++i nhanh hơn chút xíu :joy: Vậy cái vấn đề val = ở đây là sao

edit:

╭╴C:\Users\tri\Desktop
╰╼ a

TOTAL 100 TIMES MODE 2: ------------------------------------------------
++I :962060
I++ :973266

TOTAL 100 TIMES MODE 1------------------------------------------------
I++ :964262
++I :967017

chắc hên xui, 10 lần chạy cũng có 1 lần ++i nhanh hơn, nhưng hoàn toàn chậm hơn trên MSVC ko biết tại sao :disappointed:

1 Like

Proc1 và proc2 thực hiện 2 cách khác nhau nên không thể optimize thành 1 là đúng rồi.
Việc gán val thực hiện mà không optimize là nó làm đúng, nhiều khi cần như vậy tùy yêu cầu thôi.

Thời gian có thể nhanh chậm hơn nhau chút xíu vì bản chat nó như vậy rồi (xung CPU lúc cao lúc thấp,CPU switch nhiều công việc mỗi lúc lại khác nhau…)

Nhưng kết quả same same nhau cùng với việc view asm thì chắc là cũng đủ để thấy việc nhanh chậm hơn nhau giữa ++I và I++ là không có cơ sở.
Ở trên bạn disasm đã thấy việc thực hiện đều mất 4 lệnh asm. Chỉ cần vênh nhau 1 lệnh asm thì thời gian sẽ vênh nhau 20-25% rồi chứ không lệch 1 chút như vậy.

edit lần chót: đổi kiểu của dataval thành long long sẽ thấy 2 chú chạy nhanh như nhau

TOTAL 100 TIMES MODE 2: ------------------------------------------------
++I :1818525
I++ :1833648

TOTAL 100 TIMES MODE 1------------------------------------------------
I++ :1796192
++I :1794913

Kết luận là ông MSVC gặp vấn đề lớn khi convert reference của biến. Nếu chạy trên OS 32-bit chắc nó sẽ như nhau, chạy trên OS 64-bit thì reference của ++i là 8 byte nên gây ra chậm hơn int 4 byte???

thử trên MSYS2 g++ thì thấy tốc độ tương đương nhau, có ông MS là có vấn đề vì cái ++i trả về reference ổng ko handle nổi @_@

hoặc có thể trên OS 64-bit thì load lên thanh ghi bằng cách nào đó bị biến thành 64-bit integer hết? Thử với char, short, int đều thấy ++i chậm hơn, chỉ có long long là như nhau @_@

3 Likes

Xử lý số độ rộng (số byte) khác nhau chắc là cũng khác nhau rồi.
Ví dụ CPU 32 bit xử lý long (64bit) hoặc double thì nó không xử lý cái rụp được mà phải xử lý từng phần.

Code dài miên man, mà MSVC cũng đâu có theo đúng C++ 100% :slight_smile: chỉ là nghề tay trái thôi.

p/s: MSVC ko xài /O3 mà dùng /O2 /Ox /Os.

1 Like

ông Dương từ C# bẻ lái sang C++ mà, nên xài con trỏ rồi cấp phát động tùm lum =) delete[] cũng quên nữa

2 Likes

Cái gì mà bẻ, code thì code C++, cũng đang nói C++ có nói gì đến C# đâu ?
Chả thấy delete trong desctructor kia kìa :grin:
Trước giã C/C++ xong thấy cực chết mới qua C# thôi.

delete khác với delete[] =) Nếu cấp phát động cho 1 phần tử new int thì xài delete, còn 1 mảng new int[...] thì xài delete[]. Trong test mode 1 với test mode 2 đều quên delete OBJ1; delete OBJ2;, 400MB của mode 1 vẫn còn khi chạy mode 2.

xài std::vector cho lành, xài new rồi delete chi cho khổ

3 Likes

Chỗ delete nhầm :smile:
Nhưng mà chỉ chú ý vào ++I với I++ nên không để tâm mấy cái đó.
cũng không có ý định resize nên dung kiểu đó cho lẹ :smile:
Test thế đã gần full RAM rồi mà vector nữa chắc chết @@.

1 Like

lẹ đâu ko thấy, thấy phải viết dtor là 1, viết sai dtor là 2, quên delete OBJ trong testmode là 3 =) Cứ gọi Test OBJ1, OBJ2; vô tư có cần phải cấp phát động đâu, cũng vì new delete cái data nên lại sợ phải new OBJ1 luôn, phải xài vector ngay từ đầu thì cứ vô tư rồi

#include <iostream>
#include <chrono>
#include <vector>

using namespace std;
using namespace std::chrono;

const int DATA_LENGHT = 10000000;

struct Test {
    vector<long long> data;
    long long val;
    Test()  : data(DATA_LENGHT + 1), val(0) {}
    void proc1() { for (int i = 0; i < DATA_LENGHT; i++) val = data[i]++; }
    void proc2() { for (int i = 0; i < DATA_LENGHT; ++i) val = ++data[i]; }
};

void testMode1(int loop=100)
{
    Test obj1;
    Test obj2;
    long long totaltime1 = 0;
    long long totaltime2 = 0;
    for (int i = 0; i < loop; i++)
    {
        auto start = steady_clock::now();
        obj1.proc1();
        auto end = steady_clock::now();
        totaltime1 += duration_cast<microseconds>(end - start).count();

        start = steady_clock::now();
        obj2.proc2();
        end = steady_clock::now();
        totaltime2 += duration_cast<microseconds>(end - start).count();
    }
    std::cout << "TOTAL " << loop << " MODE 1 -------------------- " << endl;
    std::cout << "I++ :" << totaltime1 << "us" << endl;
    std::cout << "++I :" << totaltime2 << "us" << endl;
}

void testMode2(int loop=100)
{
    Test obj1;
    Test obj2;
    long long totaltime1 = 0;
    long long totaltime2 = 0;
    for (int i = 0; i < loop; i++)
    {
        auto start = steady_clock::now();
        obj1.proc2();
        auto end = steady_clock::now();
        totaltime2 += duration_cast<microseconds>(end - start).count();

        start = steady_clock::now();
        obj2.proc1();
        end = steady_clock::now();
        totaltime1 += duration_cast<microseconds>(end - start).count();
    }
    std::cout << "TOTAL " << loop << " MODE 2 -------------------- " << endl;
    std::cout << "I++ :" << totaltime1 << "us" << endl;
    std::cout << "++I :" << totaltime2 << "us" << endl;
}

int main()
{
    testMode1();
    testMode2();
}

ngắn gọn súc tích chính xác tới từng microsecond (nhầm qua mili @_@)

4 Likes

Chỗ code dài là phải xem xem nó làm gì trong code đó.
Thằng MSVC nó vẫn + và gán val. Chắc thằng G++ nó tang thẳng val = 50000000 luôn mới 4 lệnh. Cái này hồi làm QT Framework thấy rồi :smile:

2 Likes

Ăn bớt hết Enter của người ta ::))

3 Likes

a ơi. giải thuật toán này dùm e vs ạ. em làm như trên vẫn sai. test chương trình a+b thì ra 24.
giải thích e vs ạ. bị lộn tùng phèo chỗ này

int a=7, b=8;
a++;
a=a+(b- -);  - -b;
a- -;
a=(- -a)+(- -b);

printf("Tong cua a va b la: %d", a+b);

Undefined behavior ở dòng

rồi nhé, nghĩ làm gì nữa.

1 Like

A post was split to a new topic: Tại sao ++a ra 13?

Nói cách khác, i++ là bạn tăng biến lên 1 rồi thực hiện phép tính, còn ++i thì bạn tính rồi mới +1

hình như nói ngược rồi à? :V

5 Likes

nhầm rồi bạn, trong toán tử một ngôi prefix ++i tính trước cuối cùng mới đến postfix i++

5 Likes
        int a=7, b=8;
        a++; //a = 8
        a=a+(b--); //b = 7 a = 16  
        --b; // b = 6
        a--; //a = 15
        a=(--a)+(--b); // a = 19 và b = 5
        System.out.println("Tong cua a va b la: %d" + (a+b)); // tổng 24
1 Like
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?