Tôi đã nhìn thấy blog này:bài toán tối ưu Odd dưới MSVC
http://igoro.com/archive/gallery-of-processor-cache-effects/
Các "weirdness" trong phần 7 là những gì bắt gặp sự quan tâm của tôi.
Suy nghĩ đầu tiên của tôi là "Thats chỉ C# là lạ".
Không phải tôi đã viết mã C++ sau đây.
volatile int* p = (volatile int*)_aligned_malloc(sizeof(int) * 8, 64);
memset((void*)p, 0, sizeof(int) * 8);
double dStart = t.GetTime();
for (int i = 0; i < 200000000; i++)
{
//p[0]++;p[1]++;p[2]++;p[3]++; // Option 1
//p[0]++;p[2]++;p[4]++;p[6]++; // Option 2
p[0]++;p[2]++; // Option 3
}
double dTime = t.GetTime() - dStart;
Thời điểm tôi nhận được trên 2,4 Ghz của tôi Core 2 Quad đi như sau:
Option 1 = ~8 cycles per loop.
Option 2 = ~4 cycles per loop.
Option 3 = ~6 cycles per loop.
Bây giờ Đây là khó hiểu. Lý do của tôi đằng sau sự khác biệt đi xuống đến bộ nhớ cache ghi độ trễ (3 chu kỳ) trên chip của tôi và giả định rằng bộ nhớ cache có một cổng ghi 128-bit (Đây là công việc đoán thuần túy về phía tôi).
Trên cơ sở đó trong Tùy chọn 1: Nó sẽ tăng p [0] (1 chu kỳ) sau đó tăng p [2] (1 chu kỳ) sau đó phải đợi 1 chu kỳ (cho bộ đệm) rồi p [1] (1 chu kỳ) sau đó chờ 1 chu trình (cho bộ đệm) rồi p [3] (1 chu kỳ). Cuối cùng là 2 chu kỳ tăng và nhảy (Mặc dù nó thường được thực hiện như là giảm và nhảy). Điều này cho tổng cộng 8 chu kỳ.
Trong tùy chọn 2: Nó có thể tăng p [0] và p [4] trong một chu kỳ rồi tăng p [2] và p [6] trong một chu kỳ khác. Sau đó, 2 chu kỳ để trừ và nhảy. Không cần chờ đợi trên bộ nhớ cache. Tổng số 4 chu kỳ.
Trong tùy chọn 3: Nó có thể tăng p [0] sau đó phải đợi 2 chu kỳ sau đó tăng p [2] rồi trừ và nhảy. Vấn đề là nếu bạn đặt trường hợp 3 để tăng p [0] và p [4] nó STILL mất 6 chu kỳ (mà kinda thổi 128-bit của tôi đọc/ghi cổng ra khỏi nước).
Vậy ... có ai có thể cho tôi biết địa ngục đang diễn ra ở đây không? Tại sao trường hợp 3 mất nhiều thời gian hơn? Ngoài ra tôi rất muốn biết những gì tôi đã sai trong suy nghĩ của tôi ở trên, như tôi rõ ràng là có điều gì đó sai trái! Bất kỳ ý tưởng sẽ được nhiều đánh giá cao! :)
Nó cũng sẽ rất thú vị để xem cách GCC hoặc bất kỳ trình biên dịch khác đối phó với nó là tốt!
Chỉnh sửa: Ý tưởng của Jerry Coffin đã cho tôi một số suy nghĩ.
tôi đã thực hiện một số xét nghiệm hơn (trên một máy khác nhau để tha thứ cho sự thay đổi trong timings) có và không có nops và với số lượng khác nhau của nops
case 2 - 0.46 00401ABD jne (401AB0h)
0 nops - 0.68 00401AB7 jne (401AB0h)
1 nop - 0.61 00401AB8 jne (401AB0h)
2 nops - 0.636 00401AB9 jne (401AB0h)
3 nops - 0.632 00401ABA jne (401AB0h)
4 nops - 0.66 00401ABB jne (401AB0h)
5 nops - 0.52 00401ABC jne (401AB0h)
6 nops - 0.46 00401ABD jne (401AB0h)
7 nops - 0.46 00401ABE jne (401AB0h)
8 nops - 0.46 00401ABF jne (401AB0h)
9 nops - 0.55 00401AC0 jne (401AB0h)
Tôi đã bao gồm statetements nhảy để bạn có thể thấy rằng nguồn và đích nằm trong một dòng bộ nhớ cache. Bạn cũng có thể thấy rằng chúng tôi bắt đầu nhận được sự khác biệt khi chúng tôi cách nhau 13 byte trở lên. Cho đến khi chúng tôi đạt 16 ... thì tất cả đều sai.
Vì vậy, Jerry không đúng (mặc dù đề xuất của anh KHÔNG giúp được chút ít), tuy nhiên có điều gì đó đang diễn ra. Tôi càng bị cuốn hút và cố gắng tìm hiểu xem nó là gì bây giờ. Nó xuất hiện để được nhiều hơn một số loại liên kết bộ nhớ kỳ quặc chứ không phải là một số loại thông qua lệnh lẻ oddity.
Bất cứ ai muốn giải thích điều này cho một tâm trí tò mò? : D
Chỉnh sửa 3: Interjay có một điểm trên việc hủy đăng ký sẽ thổi chỉnh sửa trước đó ra khỏi nước. Với một vòng lặp chưa được kiểm tra, hiệu suất không cải thiện.Bạn cần phải thêm một nop vào để làm cho khoảng cách giữa nguồn nhảy và đích giống như cho số đếm tốt của tôi ở trên. Hiệu suất vẫn còn hút. Thú vị của nó là tôi cần 6 nops để cải thiện hiệu suất mặc dù. Tôi tự hỏi có bao nhiêu nops bộ vi xử lý có thể phát hành cho mỗi chu kỳ? Nếu 3 của nó sau đó là tài khoản cho bộ nhớ cache ghi độ trễ ... Nhưng, nếu thats nó, tại sao độ trễ xảy ra?
Tò mò và tò mò ...
FWIW, thật dễ dàng để có được GCC chạy trên chỉ là về bất kỳ hệ điều hành để so sánh, và bạn có thể tự do được Trình biên dịch của Intel cho một số. Cài đặt icc đã chết đơn giản đối với tôi trên Ubuntu, chỉ cần nhớ rằng bạn phải có chip Intel để tận dụng tối ưu hóa của nó. –
GCi32 là gì? – jalf
Điều duy nhất tôi có thể nghĩ đến là một số thuật toán lập lịch trình. Vì vòng lặp ngắn hơn, CPU có thể phải trì hoãn một vài chu kỳ giữa các lần lặp để đợi ghi hoàn thành, vì lý do nào đó khiến cho ** thêm chậm lại làm chậm hơn vòng lặp dài hơn.Độ trễ của bộ nhớ cache có vẻ như nó ảnh hưởng đến tất cả các trường hợp như nhau và giống như bạn nói, chiều rộng cổng R/W dường như không phải như vậy. Yếu tố duy nhất tôi có thể tưởng tượng có thể khiến vòng lặp ngắn hơn mất * lâu hơn * là một số loại giới hạn lập lịch trong CPU. – jalf