2011-11-30 32 views
7

Tôi có nhiệm vụ sau đây để chứng minh chia sẻ sai và viết một chương trình đơn giản:False chia sẻ và pthreads

#include <sys/times.h> 
#include <time.h> 
#include <stdio.h> 
#include <pthread.h> 

long long int tmsBegin1,tmsEnd1,tmsBegin2,tmsEnd2,tmsBegin3,tmsEnd3; 

int array[100]; 

void *heavy_loop(void *param) { 
    int index = *((int*)param); 
    int i; 
    for (i = 0; i < 100000000; i++) 
    array[index]+=3; 
} 

int main(int argc, char *argv[]) { 
    int  first_elem = 0; 
    int  bad_elem = 1; 
    int  good_elem = 32; 
    long long time1; 
    long long time2; 
    long long time3; 
    pthread_t  thread_1; 
    pthread_t  thread_2; 

    tmsBegin3 = clock(); 
    heavy_loop((void*)&first_elem); 
    heavy_loop((void*)&bad_elem); 
    tmsEnd3 = clock(); 

    tmsBegin1 = clock(); 
    pthread_create(&thread_1, NULL, heavy_loop, (void*)&first_elem); 
    pthread_create(&thread_2, NULL, heavy_loop, (void*)&bad_elem); 
    pthread_join(thread_1, NULL); 
    pthread_join(thread_2, NULL); 
    tmsEnd1 = clock(); 

    tmsBegin2 = clock(); 
    pthread_create(&thread_1, NULL, heavy_loop, (void*)&first_elem); 
    pthread_create(&thread_2, NULL, heavy_loop, (void*)&good_elem); 
    pthread_join(thread_1, NULL); 
    pthread_join(thread_2, NULL); 
    tmsEnd2 = clock(); 

    printf("%d %d %d\n", array[first_elem],array[bad_elem],array[good_elem]); 
    time1 = (tmsEnd1-tmsBegin1)*1000/CLOCKS_PER_SEC; 
    time2 = (tmsEnd2-tmsBegin2)*1000/CLOCKS_PER_SEC; 
    time3 = (tmsEnd3-tmsBegin3)*1000/CLOCKS_PER_SEC; 
    printf("%lld ms\n", time1); 
    printf("%lld ms\n", time2); 
    printf("%lld ms\n", time3); 

    return 0; 
} 

Tôi đã rất ngạc nhiên khi nhìn thấy kết quả (Tôi chạy nó trên bộ vi xử lý i5-430M của tôi).

  • Với chia sẻ sai, đó là 1020 ms.
  • Không chia sẻ sai, 710 giây, chỉ nhanh hơn 30% thay vì 300% (được viết trên một số trang web sẽ nhanh hơn 300-400%).
  • Nếu không sử dụng pthreads, nó là 580 ms.

Hãy cho tôi biết lỗi của tôi hoặc giải thích lý do tại sao điều đó xảy ra.

Trả lời

18

Chia sẻ sai là kết quả của nhiều lõi với bộ nhớ cache riêng biệt truy cập cùng một vùng bộ nhớ vật lý (mặc dù không phải cùng địa chỉ đó - đó sẽ là chia sẻ thực).

Để hiểu chia sẻ sai, bạn cần hiểu bộ nhớ cache. Trong hầu hết các bộ vi xử lý, mỗi lõi sẽ có bộ nhớ cache L1 của riêng nó, chứa dữ liệu được truy cập gần đây. Bộ nhớ cache được sắp xếp theo "dòng", được sắp xếp theo khối dữ liệu, thường là 32 hoặc 64 byte (tùy thuộc vào bộ xử lý của bạn). Khi bạn đọc từ một địa chỉ không có trong bộ nhớ đệm, toàn bộ dòng được đọc từ bộ nhớ chính (hoặc bộ đệm L2) vào L1. Khi bạn ghi vào một địa chỉ trong bộ nhớ cache, dòng chứa địa chỉ đó được đánh dấu là "bẩn".

Đây là nơi chia sẻ khía cạnh. Nếu nhiều lõi đang đọc từ cùng một dòng, thì mỗi lõi có một bản sao của dòng trong L1. Tuy nhiên, nếu một bản sao được đánh dấu là bẩn, nó sẽ vô hiệu hóa dòng trong các bộ đệm khác. Nếu điều này không xảy ra, sau đó viết được thực hiện trên một lõi có thể không được hiển thị cho người khác lõi cho đến sau này nhiều. Vì vậy, lần sau lõi khác đi đọc từ dòng đó, bộ nhớ cache bị mất và nó phải tìm nạp lại dòng.

Sai chia sẻ xảy ra khi các lõi đang đọc và ghi vào các địa chỉ khác nhau trên cùng một dòng. Mặc dù họ không chia sẻ dữ liệu, bộ nhớ cache hành động như họ là vì họ rất thân thiết.

Hiệu ứng này phụ thuộc nhiều vào kiến ​​trúc bộ xử lý của bạn. Nếu bạn có một bộ xử lý lõi đơn, bạn sẽ không thấy hiệu ứng nào cả, vì sẽ không có sự chia sẻ. Nếu các dòng bộ nhớ cache của bạn dài hơn, bạn sẽ thấy hiệu ứng trong cả hai trường hợp "xấu" và "tốt" vì chúng vẫn ở gần nhau. Nếu lõi của bạn không chia sẻ một bộ nhớ cache L2 (mà tôi đoán họ làm), bạn có thể thấy sự khác biệt 300-400% như bạn nói, vì họ sẽ phải đi tất cả các cách để bộ nhớ chính trên một bộ nhớ cache bỏ lỡ.

Bạn cũng có thể muốn biết rằng điều quan trọng là mỗi luồng là cả đọc và viết (+ = thay vì =). Một số bộ xử lý có ghi thông qua bộ nhớ cache có nghĩa là nếu một lõi ghi vào địa chỉ không có trong bộ nhớ cache, nó sẽ không bỏ lỡ và tìm nạp dòng từ bộ nhớ. Ngược lại điều này với ghi lại bộ nhớ cache, bỏ lỡ viết.

+0

Tôi nghĩ rằng mảng [0] và mảng [1] phải nằm trong một dòng bộ nhớ cache. Chúng rất gần, phải không? –

+0

@AlexeyMatveev: ngày 31 và 32 cũng rất gần. Nhưng bạn cho rằng chúng thuộc về các dòng bộ nhớ cache khác nhau. Sự thật là, họ có thể hoặc không thể trên cùng một dòng bộ nhớ cache. Điều gì xảy ra nếu 1 đến 5 (và mọi thứ trước 1, phù hợp) đi vào một dòng bộ nhớ cache và 6 trough 37 đi đến một bộ đệm khác? –

+0

@ Vlad-Lazarenko, tôi hiểu nó, tôi đã thử nghiệm điều này với một số khác, quá. –

2

Tôi googled về clock() chức năng trong C. Nó cung cấp cho bạn số lượng đồng hồ CPU trôi qua từ đầu đến cuối.Now khi bạn chạy hai chủ đề song song, số chu kỳ CPU sẽ là (chu kỳ đồng hồ của CPU1 + đồng hồ chu kỳ của cpu2). Tôi nghĩ rằng những gì bạn muốn là một đồng hồ hẹn giờ thực. Đối với điều này bạn sử dụng clock_gettime() và bạn sẽ nhận được kết quả mong đợi.

Tôi đã chạy mã của bạn với clock_gettime().Tôi nhận điều này:

với chia sẻ sai 874,587381 ms

mà không chia sẻ sai 331,844278 ms

tính toán tuần tự 604,160276 ms