2012-04-13 40 views
5

Tôi có một phương pháp trong thuật toán của mình chạy một vòng lặp rất chặt chẽ trên một tập dữ liệu rất lớn. Ban đầu tôi viết nó là một luồng đơn, nhưng nó mất một thời gian dài. Tôi đến mức muốn tăng tốc nó, vì vậy bây giờ tôi đang sử dụng ThreadPool để song song công việc. Vấn đề là điều này gây ra việc sử dụng CPU của tôi để đi đến 95-100%, mà tôi sắp xếp của dự kiến. Tuy nhiên, hiệu suất của tôi đã tăng lên đáng kể, nhưng tôi nghĩ rằng tôi có thể làm cho nó tốt hơn nếu tôi có thể cắt giảm tất cả các bối cảnh chuyển đổi. Điều này cũng khiến các chương trình khác của tôi hơi bị lag vì chúng phải chống lại các luồng cho tài nguyên CPU.Gọi lại ThreadPool trong vòng lặp chặt chẽ - 100% CPU

Câu hỏi của tôi là làm cách nào để tôi thực hiện việc này? Điều duy nhất tôi đã có thể nghĩ đến là giới hạn số lượng chủ đề đang chạy cùng một lúc, nhưng điều này có thể làm cho thuật toán của tôi chậm hơn vì chỉ một vài chuỗi sẽ có thể chạy cùng một lúc. Tôi không muốn thêm vào giấc ngủ trong chủ đề của mình vì tôi chỉ cần thuật toán chạy để hoàn thành càng nhanh càng tốt.

EDIT: Một số người đã đề cập sử dụng TPL. Tôi nghĩ rằng đó là một ý tưởng tuyệt vời, nhưng tiếc là tôi quên đề cập đến rằng tôi đang bị mắc kẹt bằng cách sử dụng .NET 3.5 kể từ khi ứng dụng cha mẹ đã không phát hành một phiên bản bằng cách sử dụng .NET 4 được nêu ra.

+1

Nếu bạn muốn tốc độ, tại sao bạn sẽ loại bỏ tất cả những thứ tăng tốc? Chuyển đổi ngữ cảnh được thực hiện bởi hệ điều hành, bạn không gây rối với điều đó ... – gbianchi

+1

Giải pháp là giảm ưu tiên của các chủ đề trong hồ bơi. Đây không phải là một câu trả lời bởi vì tôi không biết làm thế nào để làm điều đó một cách hiệu quả: ( –

+0

Âm thanh như bạn nên làm cho các nhiệm vụ cá nhân lớn hơn. OTOH, threadpool là đủ thông minh để đã làm hầu hết những gì bạn đề nghị. hơn so với CPU, nó sẽ xếp hàng chúng thay vì bắt đầu nhiều chủ đề hơn –

Trả lời

6

Đây là tất cả về quản lý tài nguyên. Chương trình của bạn hiện đang hogging tất cả các tài nguyên, và do đó các chương trình khác có được quyền truy cập vào chúng. Bạn cần phải cân bằng "Tôi chỉ cần các thuật toán để chạy đến hoàn thành càng nhanh càng tốt" một phần với "Điều này cũng gây ra các chương trình khác của tôi là một chút laggy vì họ có để chống lại các chủ đề cho tài nguyên CPU". Họ là loại trừ lẫn nhau; bạn không thể chạy ứng dụng của mình nhanh như nó có thể có trên một máy cụ thể và cũng giữ cho các ứng dụng khác hoàn toàn đáp ứng. Chỉ đơn giản là một giới hạn cho bao nhiêu CPU có thể làm trong bất kỳ khoảng thời gian nào.

Theo như hiệu quả đạt được, có một vài điều bạn có thể làm:

  • Không sử dụng ThreadPool cho thuật toán cực kỳ tối ưu hóa luồng. ThreadPool là tuyệt vời cho đơn giản "Đi và làm điều này và cho tôi biết bạn đang thực hiện" hoạt động. Tuy nhiên, nếu bạn đang tìm kiếm để tối ưu hóa, các chi phí vốn có trong việc thêm một mức độ bổ sung của lập kế hoạch thread với ThreadPool (trên đầu trang của overhead vốn có trong CPU và hệ điều hành) có thể tránh được. Bạn cũng có quyền kiểm soát hạn chế hơn đối với luồng trong ThreadPool, nghĩa là tối ưu hóa như gán ái lực bộ xử lý (để cân bằng tải) và ưu tiên (để cung cấp chuỗi thời gian nhiều hơn hoặc ít hơn) của các chuỗi riêng lẻ không có sẵn.Hãy thử tạo các Threads đơn giản, hoặc nhìn vào TPL trong đó có một số chiến lược để có được nhiều thứ được thực hiện (không phải tất cả đều yêu cầu luồng đầu tiên).

  • Có, bạn sẽ muốn có thể "tăng tốc" số chuỗi. Đây là cả hai để cho phép các chương trình khác một số CPU thời gian bằng cách giảm nhu cầu của chương trình của bạn cho nó, nhưng như tôi đã nói, đó cũng là trên vốn có trong đa luồng. Quy tắc chung là nếu CPU được tăng gấp đôi số chủ động đang chạy vì nó có "đơn vị thực thi" (đây là lõi vật lý trên chip CPU và "bộ xử lý logic" như công nghệ HyperThreading tách một lõi thành hai), sau đó hệ điều hành sẽ dành nhiều thời gian lên lịch trình và chuyển đổi giữa chúng ("cache-thrashing") hơn là nó sẽ chi tiêu thực sự chạy các chủ đề. Nói một cách tổng quát hơn, có một định luật về lợi nhuận giảm dần, điều này sẽ tiến triển thành "sự không cân xứng về quy mô"; cuối cùng, việc thêm một chuỗi khác sẽ khiến chương trình của bạn chạy chậm hơn nếu bạn không sử dụng chuỗi đó. Có, ThreadPool xử lý các chủ đề tối đa cho bạn, nhưng đó có lẽ là tính năng đơn giản nhất của nó để thực hiện chính mình trong thuật toán của riêng bạn.

  • Đảm bảo rằng công việc của từng luồng được tối ưu hóa. Tìm các thuật toán ngây thơ hoặc không hiệu quả (tôi gọi chúng là "O (My God) -complexity") và sắp xếp chúng. Có một giới hạn thấp hơn về hiệu quả của hầu hết các hoạt động (nó thay đổi theo loại hoạt động), và "tối ưu hóa sớm là gốc rễ của tất cả các điều ác" (không tối ưu hóa hiệu suất với chi phí làm cho mã thực sự hoạt động), nhưng hiểu rằng trong môi trường đa luồng, mọi lợi ích bạn có thể thực hiện trên hiệu quả của thuật toán khi chạy một lần sẽ được nhân với số lần bạn đang chạy nó, vì vậy đảm bảo hoạt động song song hiệu quả là tiền thưởng kép.

+0

+1 chỉ dành cho O (My God) thôi - câu trả lời tuyệt vời ;-) – BrokenGlass

+1

'Quy tắc chung là nếu một CPU được cho nhiều hơn gấp đôi số lượng chủ động đang chạy vì nó có "các đơn vị thực hiện" (đây là các lõi vật lý trên chip CPU và "bộ vi xử lý hợp lý" như công nghệ HyperThreading tách một lõi thành hai), sau đó hệ điều hành sẽ dành nhiều thời gian lên lịch và chuyển đổi giữa chúng ("cache-thrashing")) hơn là nó sẽ chi tiêu thực sự chạy các chủ đề '- có bạn thực sự đã cố gắng này? Trên mã không được quản lý, nó không tạo ra bất kỳ sự khác biệt nào cho dù bạn có 8 luồng CPU hay 800 - xấp xỉ cùng một lượng công việc được thực hiện. –

+0

Vì vậy, nếu tôi có một CPU i7 lõi ​​(4 lõi vật lý + 4 lõi ảo), 16 luồng là giới hạn theo quy tắc đó? –

2

Nếu bạn có thể viết lại ứng dụng chính của mình thành vòng foreach qua IEnumerable, bạn có thể sử dụng PLINQ để song song vòng lặp của mình. Bạn có thể sử dụng WithDegreeOfParallelism để kiểm soát số lượng lõi ứng dụng của bạn sẽ sử dụng. Bạn có thể ngăn chặn một số "lag" bạn gặp phải bằng cách không sử dụng tất cả các lõi trên máy tính của bạn. Ngoài ra, bạn không phải giải quyết cách phân vùng vòng lặp của bạn trên các luồng để tránh tranh chấp tài nguyên không cần thiết. PLINQ làm tất cả những gì cho bạn.

Giả sử bạn có rất đơn giản đơn luồng vòng lặp này:

var arrayOfStuff = new[] { ... }; 
for (var i = 0; i < arrayOfStuff.Length; ++i) 
    DoSomething(arrayOfStuff[i]); 

Nếu đặt hàng không quan trọng bạn có thể parallelize nó bằng cách sử PLINQ sử dụng một lõi nhỏ hơn có sẵn:

var cores = Math.Max(1, Environment.ProcessorCount - 1); 
arrayOfStuff.AsParallel().WithDegreeOfParallelism(cores).ForAll(DoSomething); 

Thậm chí nếu vòng lặp chính của bạn phức tạp hơn, bạn có thể viết lại nó thành khối lặp mà bạn có thể song song:

IEnumerable<Stuff> GetStuff() { 
    for (... very complex looping ...) { 
    ... 
    yield return stuff; 
    } 
} 
Các vấn đề liên quan