2016-09-01 14 views
8

Tôi đã lược tả một đoạn mã nhỏ là một phần của mô phỏng lớn hơn, và ngạc nhiên của tôi, hàm STL bằng (std :: equal) chậm hơn nhiều so với một vòng lặp đơn giản, so sánh hai phần tử mảng theo phần tử . Tôi đã viết một trường hợp thử nghiệm nhỏ, mà tôi tin là một sự so sánh công bằng giữa hai, và sự khác biệt, bằng cách sử dụng g + + 6.1.1 từ lưu trữ Debian không phải là không đáng kể. Tôi đang so sánh hai, bốn phần tử mảng của các số nguyên đã ký. Tôi đã thử nghiệm std :: bằng, toán tử == và một vòng lặp nhỏ. Tôi đã không sử dụng std :: chrono cho một thời gian chính xác, nhưng sự khác biệt có thể được nhìn thấy một cách rõ ràng với thời gian.tại sao là std :: bằng nhiều chậm hơn so với một vòng tay cán cho hai nhỏ :: mảng?

Câu hỏi của tôi là, được cung cấp mã mẫu bên dưới, tại sao toán tử == và hàm bị quá tải std :: bằng (mà gọi toán tử == tôi tin) mất khoảng 40 giây để hoàn tất và vòng viết tay chỉ mất 8 giây ? Tôi đang sử dụng một máy tính xách tay intel rất gần đây. Vòng lặp for nhanh hơn trên tất cả các mức tối ưu hóa, -O1, -O2, -O3 và -Ofast. Tôi biên soạn mã với g++ -std=c++14 -Ofast -march=native -mtune=native

Run the code

Các vòng lặp chạy một số lượng lớn các lần, chỉ để tạo sự khác biệt rõ ràng bằng mắt thường. Các toán tử modulo đại diện cho một hoạt động giá rẻ trên một trong các phần tử mảng và phục vụ để giữ trình biên dịch không tối ưu hóa vòng lặp.

#include<iostream> 
#include<algorithm> 
#include<array> 

using namespace std; 
using T = array<int32_t, 4>; 

bool 
are_equal_manual(const T& L, const T& R) 
noexcept { 
    bool test{ true }; 
    for(uint32_t i{0}; i < 4; ++i) { test = test && (L[i] == R[i]); } 
    return test; 
} 

bool 
are_equal_alg(const T& L, const T& R) 
noexcept { 
    bool test{ equal(cbegin(L),cend(L),cbegin(R)) }; 
    return test; 
} 

int main(int argc, char** argv) { 

    T left{ {0,1,2,3} }; 
    T right{ {0,1,2,3} }; 

    cout << boolalpha << are_equal_manual(left,right) << endl; 
    cout << boolalpha << are_equal_alg(left,right) << endl; 
    cout << boolalpha << (left == right) << endl; 

    bool t{}; 
    const size_t N{ 5000000000 }; 
    for(size_t i{}; i < N; ++i) { 
     //t = left == right; // SLOW 
     //t = are_equal_manual(left,right); // FAST 
     t = are_equal_alg(left,right); // SLOW 
     left[0] = i % 10; 
     right[2] = i % 8; 
    } 

    cout<< boolalpha << t << endl; 

    return(EXIT_SUCCESS); 
} 
+0

Không thể tạo lại kết quả của bạn. Cả ba phiên bản đều có cùng hiệu suất, mặc dù tôi mong đợi bạn sẽ chậm hơn vì nó luôn chạy vòng lặp đến cuối (trong khi nó có thể dừng ngay khi nó chạm vào một cặp phần tử không so sánh bằng nhau). – Leon

+1

[Đầu ra lắp ráp] (https://godbolt.org/g/L5ioi4), so sánh thủ công là chưa được kiểm soát cmpl, trong khi 'bằng' và' == 'đang sử dụng' memcmp' –

+0

Leon - Tôi đã có thể tái tạo nó trên coliru cũng. Bạn đã thử chạy mã với liên kết tôi đã cung cấp chưa? Bạn đang sử dụng phiên bản GCC nào và trên nền tảng nào? – KBentley57

Trả lời

1

Đây là lắp ráp tạo của for vòng lặp trong main() khi are_equal_manual(left,right) chức năng được sử dụng:

.L21: 
     xor  esi, esi 
     test eax, eax 
     jne  .L20 
     cmp  edx, 2 
     sete sil 
.L20: 
     mov  rax, rcx 
     movzx esi, sil 
     mul  r8 
     shr  rdx, 3 
     lea  rax, [rdx+rdx*4] 
     mov  edx, ecx 
     add  rax, rax 
     sub  edx, eax 
     mov  eax, edx 
     mov  edx, ecx 
     add  rcx, 1 
     and  edx, 7 
     cmp  rcx, rdi 

Và đây là những gì được tạo ra khi are_equal_alg(left,right) chức năng được sử dụng:

.L20: 
     lea  rsi, [rsp+16] 
     mov  edx, 16 
     mov  rdi, rsp 
     call memcmp 
     mov  ecx, eax 
     mov  rax, rbx 
     mov  rdi, rbx 
     mul  r12 
     shr  rdx, 3 
     lea  rax, [rdx+rdx*4] 
     add  rax, rax 
     sub  rdi, rax 
     mov  eax, ebx 
     add  rbx, 1 
     and  eax, 7 
     cmp  rbx, rbp 
     mov  DWORD PTR [rsp], edi 
     mov  DWORD PTR [rsp+24], eax 
     jne  .L20 

tôi 'không chính xác chắc chắn những gì đang xảy ra trong mã được tạo ra cho trường hợp đầu tiên, nhưng rõ ràng là không gọi memcmp(). Nó không xuất hiện để được so sánh các nội dung của các mảng ở tất cả. Trong khi vòng lặp vẫn đang được lặp lại 5000000000 lần, nó được tối ưu hóa để không làm gì nhiều. Tuy nhiên, vòng lặp sử dụng are_equal_alg(left,right) vẫn đang thực hiện so sánh. Về cơ bản, trình biên dịch vẫn có thể tối ưu hóa việc so sánh thủ công tốt hơn nhiều so với mẫu std::equal.

+0

Michael - Tôi đã sử dụng toán tử mô đun để mô phỏng một cuộc gọi đến những gì cơ bản là rand() trên một trong các phần tử cho mỗi vòng lặp lặp lại.Nếu có modulus không đủ để giữ trình biên dịch tối ưu hóa một thứ gì đó, tôi có thể thử chỉnh sửa và đăng một phiên bản thực tế hơn. – KBentley57

+0

@ KBentley57: vòng lặp vẫn đang lặp lại - Tôi nghĩ vấn đề là Jesse Good và bạn nhận thấy: rằng 'std :: equal <>()' dường như không so sánh trực tiếp các kiểu cơ bản khi nó có lẽ nên. Tôi đã không thực hiện công việc để theo dõi việc thực hiện gcc's 'std :: equal', nhưng nó có vẻ dựa vào' memcmp() 'trong trường hợp này khi có thể là cách nó có thể tránh được đối với mảng (hoặc vectơ)/các thùng chứa khác?) của các loại cơ bản như bạn đã lưu ý trong nhận xét ở trên. –

+0

Tôi tin rằng T.C. đã đăng triển khai và nó dường như được mặc định là memcmp. Bạn có nghĩ rằng đây là báo cáo lỗi xứng đáng, hay chỉ là cách nó dành cho các loại cơ bản? – KBentley57

Các vấn đề liên quan