2013-08-04 47 views
8

Khi biên dịch đoạn mã sauđiều khiển thứ tự xuất chậm lại đa luồng chương trình

#include <iostream> 
#include <vector> 
#include <thread> 
#include <chrono> 
#include <mutex> 

std::mutex cout_mut; 

void task() 
{ 
    for(int i=0; i<10; i++) 
    { 
     double d=0.0; 
     for(size_t cnt=0; cnt<200000000; cnt++) d += 1.23456; 

     std::lock_guard<std::mutex> lg(cout_mut); 
     std::cout << d << "(Help)" << std::endl; 
     //  std::cout << "(Help)" << d << std::endl; 
    } 
} 

int main() 
{ 
    std::vector<std::thread> all_t(std::thread::hardware_concurrency()); 

    auto t_begin = std::chrono::high_resolution_clock::now(); 

    for(auto& t : all_t) t = std::thread{task}; 
    for(auto& t : all_t) t.join(); 

    auto t_end = std::chrono::high_resolution_clock::now(); 

    std::cout << "Took : " << (t_end - t_begin).count() << std::endl; 
} 

Dưới MinGW 4.8.1 phải mất khoảng 2,5 giây để thực thi trên hộp của tôi. Đó là khoảng thời gian cần thiết để chỉ thực hiện chức năng task đơn luồng.

Tuy nhiên, khi tôi bỏ ghi chú dòng ở giữa và do đó nhận xét ra các dòng trước (có nghĩa là, khi tôi trao đổi thứ tự mà d"(Help)" được ghi vào std::cout) toàn bộ điều giờ đây chỉ mất 8-9 giây.

Giải thích là gì?

Tôi đã kiểm tra lại và phát hiện ra rằng tôi chỉ gặp sự cố với MinGW-build x32-4.8.1-win32-dwarf-rev3 nhưng không phải với MinGW xây dựng x64-4.8.1-posix-seh-rev3. Tôi có một máy 64-bit. Với trình biên dịch 64 bit, cả hai phiên bản đều mất ba giây. Tuy nhiên, bằng cách sử dụng trình biên dịch 32 bit, vấn đề vẫn còn (và không phải là do sự nhầm lẫn phiên bản gỡ lỗi/phát hành).

+1

thể nó bằng cách nào đó đã xảy ra mà bạn có thời gian nhanh từ bản phát hành bản phát hành và thời gian chậm từ bản dựng gỡ lỗi? Tôi không nhận được bất kỳ sự khác biệt đáng kể nào bằng cách hoán đổi, với gcc 4.7.2/4.8.1, clang 3.2/3.3 (tất cả Linux). Nhưng đối với bản phát hành, tôi nhận được khoảng 2 giây và để gỡ lỗi tôi nhận được khoảng 8. Đáng chú ý là tỷ lệ bạn đang thấy (trên Windows, trên một số máy khác). –

+0

Không, nó không phải là một vấn đề gỡ lỗi/phát hành; vui lòng xem chỉnh sửa của tôi ở trên. –

+0

Có vẻ như một sự khác biệt của x87 fpu cho 32 bit và SSE2 cho 64 bit. Trình biên dịch 32 bit có thể tạo ra các mã lệnh (chậm) x87 của các phép toán 'double'. – yohjp

Trả lời

1

Không có gì liên quan đến đa luồng. Đó là một vấn đề tối ưu hóa vòng lặp. Tôi đã sắp xếp lại mã gốc để có được một cái gì đó Minimalistic chứng minh vấn đề:

#include <iostream> 
#include <chrono> 
#include <mutex> 

int main() 
{ 
    auto t_begin = std::chrono::high_resolution_clock::now(); 
    for(int i=0; i<2; i++) 
    { 
     double d=0.0; 
     for(int j=0; j<100000; j++) d += 1.23456; 
     std::mutex mutex; 
     std::lock_guard<std::mutex> lock(mutex); 
#ifdef SLOW 
     std::cout << 'a' << d << std::endl; 
#else 
     std::cout << d << 'a' << std::endl; 
#endif 
    } 
    auto t_end = std::chrono::high_resolution_clock::now(); 
    std::cout << "Took : " << (static_cast<double>((t_end - t_begin).count())/1000.0) << std::endl; 
} 

Khi biên soạn và thực hiện và với:

Đầu ra là:

a123456 
a123456 
Took : 931 
123456a 
123456a 
Took : 373 

Hầu hết các khác biệt trong thời gian được giải thích bởi các mã lắp ráp tạo ra cho vòng lặp bên trong: trường hợp nhanh tích lũy trực tiếp trong xmm0 trong khi trường hợp chậm tích lũy vào xmm1 - dẫn đến 2 thêm movsd instru ctions.

Bây giờ, khi biên soạn với các tùy chọn '-ftree-loop-linear':

g++ -std=c++11 -ftree-loop-linear -DSLOW -o slow -O3 b.cpp -lpthread ; g++ -std=c++11 -ftree-loop-linear -o fast -O3 b.cpp -lpthread ; ./slow ; ./fast 

Sản lượng trở thành:

a123456 
a123456 
Took : 340 
123456a 
123456a 
Took : 346