2011-12-08 29 views
7

Kịch bản ứng dụng của tôi là như thế này: Tôi muốn đánh giá hiệu năng đạt được trên máy tính lõi tứ để xử lý cùng một lượng dữ liệu. Tôi có hai cấu hình sau:Cần suy nghĩ về lược tả đa luồng trong C trên Linux

i) 1-Process: Một chương trình không có luồng và xử lý dữ liệu từ 1M .. 1G, trong khi hệ thống được giả định chỉ chạy lõi đơn của lõi 4 nhân của nó.

ii) 4-threads-Process: Một chương trình có 4 luồng (tất cả các chuỗi hoạt động giống nhau) nhưng xử lý 25% dữ liệu đầu vào.

Trong chương trình tạo 4 chuỗi, tôi đã sử dụng các tùy chọn mặc định của pthread (tức là, không có bất kỳ pthread_attr_t cụ thể nào). Tôi tin rằng hiệu suất đạt được của cấu hình 4-thread so với cấu hình 1-Process nên gần gũi hơn với 400% (hoặc một nơi nào đó giữa 350% và 400%).

tôi cấu hình thời gian dành cho sự sáng tạo của đề giống như dưới đây:

timer_start(&threadCreationTimer); 
pthread_create(&thread0, NULL, fun0, NULL); 
pthread_create(&thread1, NULL, fun1, NULL); 
pthread_create(&thread2, NULL, fun2, NULL); 
pthread_create(&thread3, NULL, fun3, NULL); 
threadCreationTime = timer_stop(&threadCreationTimer); 

pthread_join(&thread0, NULL); 
pthread_join(&thread1, NULL); 
pthread_join(&thread2, NULL); 
pthread_join(&thread3, NULL);  

Kể từ khi tăng kích thước của dữ liệu đầu vào cũng có thể tăng lên trong những yêu cầu bộ nhớ của mỗi chủ đề, sau đó để tải tất cả các dữ liệu trước là chắc chắn không phải là một lựa chọn khả thi. Do đó, để đảm bảo không làm tăng yêu cầu bộ nhớ của mỗi luồng, mỗi luồng đọc dữ liệu theo các khối nhỏ, xử lý nó và đọc quá trình xử lý tiếp theo và vv. Do đó, cấu trúc của mã các chức năng của tôi chạy theo chủ đề là như thế này:

timer_start(&threadTimer[i]); 
while(!dataFinished[i]) 
{ 
    threadTime[i] += timer_stop(&threadTimer[i]); 
    data_source(); 
    timer_start(&threadTimer[i]); 
    process(); 
} 
threadTime[i] += timer_stop(&threadTimer[i]); 

Biến dataFinished[i] được đánh dấu true bởi quá trình khi nó nhận được và xử lý tất cả dữ liệu cần thiết. Process() biết khi nào để làm điều đó :-)

Trong hàm main, Tôi đang tính toán thời gian thực hiện bởi cấu hình 4-luồng như sau:

execTime4Thread = max(threadTime[0], threadTime[1], threadTime[2], threadTime[3]) + threadCreationTime.

Và đạt được hiệu suất được tính bằng cách đơn giản

gain = execTime1process/execTime4Thread * 100

Vấn đề: On kích thước dữ liệu nhỏ xung quanh 1M để 4M, đạt được hiệu suất nói chung là tốt (giữa 350% đến 400%). Tuy nhiên, xu hướng tăng hiệu suất là giảm theo cấp số nhân với sự gia tăng kích thước đầu vào. Nó tiếp tục giảm cho đến khi một số kích thước dữ liệu tối đa 50M hoặc hơn, và sau đó trở nên ổn định khoảng 200%. Một khi nó đạt đến điểm đó, nó vẫn gần như ổn định cho dù chỉ 1GB dữ liệu.

Câu hỏi của tôi là ai cũng có thể đề xuất lý do chính của hành vi này (tức là hiệu suất giảm khi bắt đầu và sau đó vẫn ổn định sau này)?

Và đề xuất cách khắc phục điều đó?

Để biết thông tin của bạn, tôi cũng đã điều tra hành vi của threadCreationTimethreadTime cho mỗi chuỗi để xem điều gì đang xảy ra. Đối với 1M dữ liệu, giá trị của các biến này nhỏ và tăng kích thước dữ liệu cả hai biến này tăng theo cấp số nhân (nhưng threadCreationTime sẽ vẫn gần như giống nhau bất kể kích thước dữ liệu và threadTime sẽ tăng với tốc độ tương ứng với dữ liệu đang được xử lý).Sau khi tiếp tục tăng cho đến khi 50M hoặc hơn threadCreationTime trở nên ổn định và threadTime (giống như giảm hiệu suất trở nên ổn định) và threadCreationTime tiếp tục tăng với tốc độ không đổi tương ứng với việc tăng dữ liệu cần xử lý (được coi là dễ hiểu).

Bạn có nghĩ rằng tăng kích thước ngăn xếp của từng chuỗi, xử lý thông tin ưu tiên hoặc giá trị tùy chỉnh của các loại thông số khác của trình lên lịch (sử dụng pthread_attr_init) có thể giúp bạn không?

PS: Các kết quả thu được khi chạy các chương trình trong chế độ không an toàn của Linux có gốc (nghĩa là, HĐH tối thiểu đang chạy mà không có GUI và công cụ mạng).

+0

Mô hình CPU của bạn là gì? – Tudor

+4

Nhiều khả năng ô nhiễm chéo giữa bộ nhớ cache giữa các chủ đề. Bạn đã thử thay đổi kích thước của các khối dữ liệu chưa? Bạn cũng nên bao gồm việc tải dữ liệu trong các phép đo của mình vì nó có thể là nút cổ chai, tức là 2 lõi có thể làm bão hòa bus nhớ của bạn. (Ngoài ra, nếu bạn không làm điều đó, bạn nên đặt bộ hẹn giờ của bạn trên các dòng bộ nhớ cache khác nhau.) – Mats

+0

@Mats: Bộ vi xử lý là Intel (R) Core (TM) 2 CPU Quad Q9950 @ 2.83GHz. Không, tôi chưa xác minh kích thước của đoạn dữ liệu. OK, tôi sẽ cố gắng thay đổi kích thước của đoạn dữ liệu. Tuy nhiên, tôi không hiểu ý bạn là gì bởi các dòng bộ nhớ cache. Làm cách nào để đặt bộ hẹn giờ vào bộ nhớ cache? – Junaid

Trả lời

2

Kể từ khi tăng kích thước của dữ liệu đầu vào cũng có thể tăng trong yêu cầu bộ nhớ của mỗi chủ đề, sau đó tải tất cả các dữ liệu trước chắc chắn không phải là một lựa chọn khả thi. Do đó, để đảm bảo không phải để tăng yêu cầu bộ nhớ của mỗi luồng, mỗi luồng đọc dữ liệu trong các đoạn nhỏ, xử lý và đọc đoạn tiếp theo và như vậy.

Chỉ riêng điều này, có thể gây ra sự sụt giảm mạnh mẽ giảm tốc độ.

Nếu có đủ bộ nhớ, đọc một đoạn lớn dữ liệu đầu vào sẽ luôn nhanh hơn dữ liệu đọc trong các đoạn nhỏ, đặc biệt là từ mỗi chuỗi. Bất kỳ lợi ích I/O từ chunking (hiệu ứng bộ nhớ đệm) biến mất khi bạn phá vỡ nó thành từng mảnh. Ngay cả việc phân bổ một phần lớn bộ nhớ cũng rẻ hơn nhiều so với phân bổ các khối nhỏ nhiều lần.

Khi kiểm tra tính chính xác, bạn có thể chạy htop để đảm bảo rằng ít nhất tất cả lõi của bạn đang được đứng đầu trong khi chạy. Nếu không, nút cổ chai của bạn có thể nằm ngoài mã đa luồng của bạn.

Trong luồng,

  • bối cảnh luồng tắc do nhiều chủ đề có thể gây tăng tốc tối ưu phụ
  • như đã đề cập bởi những người khác, một bộ nhớ cache lạnh do không đọc bộ nhớ liên tục kế nhau có thể gây ra chậm

Nhưng đọc lại OP của bạn, tôi nghi ngờ sự chậm lại có liên quan đến việc phân bổ dữ liệu vào/bộ nhớ của bạn. Bạn đọc dữ liệu của mình chính xác ở đâu? Một số loại ổ cắm? Bạn có chắc chắn cần phân bổ bộ nhớ nhiều hơn một lần trong chuỗi của mình không?

Một số thuật toán trong chuỗi công việc của bạn có thể có giá trị dưới mức tối ưu/tốn kém.

0

Chủ đề của bạn có bắt đầu từ quá trình tạo không? Nếu đúng như vậy, sau đó sẽ xảy ra:

trong khi chuỗi chủ đề của bạn đang tạo chuỗi, chuỗi đã tạo sẽ bắt đầu chạy. Khi bạn nhấn timerStop (bộ hẹn giờ ThreadCreation), bốn đã chạy trong một thời gian nhất định. Vì vậy, threadCreationTime chồng chéo threadTime[i]

Hiện tại, bạn không biết mình đang đo lường điều gì. Điều này sẽ không giải quyết vấn đề của bạn, bởi vì rõ ràng bạn có một vấn đề vì threadTime không tăng thêm tuyến tính, nhưng ít nhất bạn sẽ không thêm thời gian trùng lặp.

Để biết thêm thông tin, bạn có thể sử dụng perf tool nếu có sẵn trên bản phân phối của bạn. ví dụ:

perf stat -e cache-misses <your_prog> 

và xem những gì sẽ xảy ra với một phiên bản hai luồng, một phiên bản ba chủ đề vv ...

+0

Vấn đề này vẫn còn đó ngay cả khi tôi không xem xét 'threadCreationTime' và chỉ xem xét' threadTime [i] '(mà bây giờ đã được chia thành các biến riêng biệt theo các gợi ý ở trên trên các dòng bộ đệm). Sau khi làm theo gợi ý đó, kết quả được cải thiện nhưng bây giờ nút cổ chai đã bị dịch chuyển. Tức là, trên dữ liệu 1M, hiệu suất đạt được là tốt. Nhưng vào 2M nó giảm xuống và sau đó vẫn giữ nguyên cho cả 1G. Tôi cũng sẽ thử đề xuất của bạn để xem các lần xóa bộ nhớ cache. Bạn có nghĩ rằng valgrind có thể giúp đỡ? Tôi cũng đang cố gắng thử trình biên dịch Intel vTune. – Junaid

+0

@Junaid: bộ nhớ cache-nhớ chỉ là một ví dụ, có rất nhiều truy cập để xem. – shodanex

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