Tải thử nghiệm: làm thế nào để tạo mỗi yêu cầu thứ hai?


14

Tôi có một thành phần máy chủ chạy trên Zeroc-ICE. Khi tôi muốn tải thử nghiệm, tôi nghĩ rằng sử dụng thư viện song song để tạo nhiều yêu cầu sẽ làm điều đó. Nhưng nó không kết thúc theo cách đó. Sử dụng thư viện Parallel (Parallel.For) từ C # rõ ràng là dễ dàng hơn nhưng dường như không chính xác tạo ra mọi thứ song song trong cùng một lúc. Vì vậy, nó không thể là định nghĩa để tạo N yêu cầu mỗi giây. Tôi nên làm thế nào? Tôi đoán bất cứ ai muốn làm thử tải trước tiên sẽ thực sự nghĩ về điều này.

  1. Cách hiệu quả để thực sự tạo N yêu cầu trong thực sự mỗi giây là gì?

  2. Một huyền thoại khác là về lập trình song song. Hãy khai sáng cho chúng tôi, nếu bạn đã sử dụng các mẫu lập trình song song trong C # hoặc .Net nói chung. Hãy tưởng tượng tôi có 5 quy trình. Làm thế nào sẽ bắt đầu tất cả năm quá trình cùng một lúc. Điều đó có nghĩa gì với việc tiêu thụ tài nguyên của tôi? Tôi đã cố gắng đọc nhiều tài liệu có sẵn trên mạng nhưng tôi nhận được nhiều câu hỏi hơn là câu trả lời cho câu hỏi của tôi.

  3. Tôi đã sử dụng Parallel.For và tạo N chủ đề và đo thời gian. Sau đó, tôi đã thử điều tương tự bằng cách sử dụng Task.Factory.start để liệt kê các nhiệm vụ. Thời gian đo là khác nhau. Vì vậy, chính xác sự khác biệt giữa việc sử dụng này là gì? Khi nào tôi nên sử dụng các lớp tương ứng và cho mục đích chính xác là gì? chúng ta thường có rất nhiều sự giàu có nhưng chính xác là chúng ta không biết cách phân biệt cái này với cái khác. Đây là một trường hợp như vậy đối với tôi, không thể tìm thấy lý do tại sao tôi không nên sử dụng cái này từ cái khác.

  4. Tôi đã sử dụng lớp đồng hồ bấm giờ để đo những lần này là tốt nhất. Trong kịch bản tôi tải thử nghiệm một thành phần, đâu sẽ là cách để đo thời gian phản hồi. Đồng hồ bấm giờ dường như là giải pháp tốt nhất cho tôi. Mọi ý kiến ​​đều được chào đón.

ps: Có nhiều công cụ kiểm tra tải cho các ứng dụng web. Của tôi là một trường hợp tùy chỉnh của các thành phần máy chủ. Và câu hỏi của tôi liên quan nhiều hơn đến việc tạo N chủ đề mỗi giây.

Tất cả các ý kiến ​​đều được chào đón. Chỉ cần đừng nghĩ rằng nó không phải là một câu hỏi lập trình. Đó là tất nhiên. Nó nên rung chuông cho bất kỳ lập trình viên nào muốn tự mình tìm hiểu về hiệu suất của sản phẩm. Tôi đã thử nhiều lựa chọn và sau đó tôi phải thực sự làm thế nào?


Faq nói rằng nếu nó liên quan đến một vấn đề lập trình cụ thể và nếu đó là một vấn đề thực tế có thể trả lời được trong nghề lập trình, thì có thể hỏi. những người hoài nghi và gắn cờ này. hãy bình luận.
Vua

Bạn có ý nghĩa gì bởi "cùng một lúc"? Tôi tự hỏi nếu bạn có thể buộc TPL hoặc PLinq bằng mọi cách để đạt được điều đó.
Gert Arnold

Câu hỏi của tôi là về việc tạo N yêu cầu mỗi giây. Vì vậy, ngay lập tức trong kịch bản này có nghĩa là theo sự hiểu biết của tôi về việc sử dụng song song sẽ bắt đầu các luồng theo cách song song.
Vua

Bạn đã thực hiện bất kỳ phân tích tuần tự?

3
Nó có thể liên quan đến lập trình, nhưng có quá nhiều câu hỏi trong bài viết của bạn (ít nhất là 4). Tôi sẽ giảm nó thành một câu hỏi mà bạn muốn hỏi trước khi nó bị đóng vì nó quá rộng. Cung cấp thông tin liên quan, như 10000 bạn chỉ vừa đề cập, số lõi trong máy thử nghiệm của bạn). Hiển thị mã thường giúp.
Gert Arnold

Câu trả lời:


10

Tôi không có tất cả các câu trả lời. Hy vọng rằng tôi có thể đổ một số ánh sáng trên đó.

Để đơn giản hóa các tuyên bố trước đây của tôi về các mô hình luồng của .NET, chỉ cần biết rằng Thư viện song song sử dụng Tác vụ và Trình quản lý tác vụ mặc định cho Tác vụ, sử dụng ThreadPool. Bạn càng lên cao trong hệ thống phân cấp (ThreadPool ở dưới cùng), bạn càng có nhiều chi phí khi tạo các mục. Chi phí hoạt động thêm đó chắc chắn không có nghĩa là nó chậm hơn, nhưng thật tốt khi biết rằng nó ở đó. Cuối cùng, hiệu suất của thuật toán của bạn trong một môi trường đa luồng đi xuống thiết kế của nó. Những gì thực hiện tốt tuần tự có thể không thực hiện tốt song song. Có quá nhiều yếu tố liên quan để cung cấp cho bạn các quy tắc cứng và nhanh, chúng thay đổi tùy thuộc vào những gì bạn đang cố gắng thực hiện. Vì bạn đang xử lý các yêu cầu mạng, tôi sẽ thử và đưa ra một ví dụ nhỏ.

Hãy để tôi nói rằng tôi không phải là chuyên gia về ổ cắm, và tôi không biết gì về Zeroc-Ice. Tôi biết về bit về các hoạt động không đồng bộ, và đây là nơi nó sẽ thực sự giúp bạn. Nếu bạn gửi yêu cầu đồng bộ qua ổ cắm, khi bạn gọi Socket.Receive(), chuỗi của bạn sẽ chặn cho đến khi nhận được yêu cầu. Điều này không tốt. Chủ đề của bạn không thể thực hiện thêm bất kỳ yêu cầu nào kể từ khi nó bị chặn. Sử dụng Socket.Beginxxxxxx (), yêu cầu I / O sẽ được thực hiện và đưa vào hàng đợi IRP cho ổ cắm và luồng của bạn sẽ tiếp tục. Điều này có nghĩa là, chủ đề của bạn thực sự có thể thực hiện hàng ngàn yêu cầu trong một vòng lặp mà không có bất kỳ chặn nào cả!

Nếu tôi hiểu bạn chính xác, bạn đang sử dụng các cuộc gọi qua Zeroc-Ice trong mã thử nghiệm của mình, không thực sự cố gắng đạt đến điểm cuối http. Nếu đó là trường hợp, tôi có thể thừa nhận rằng tôi không biết Zeroc-Ice hoạt động như thế nào. Tuy nhiên, tôi sẽ đề nghị làm theo lời khuyên được liệt kê ở đây , đặc biệt là phần : Consider Asynchronous Method Invocation (AMI). Trang này hiển thị điều này:

Bằng cách sử dụng AMI, máy khách sẽ lấy lại chuỗi điều khiển ngay khi lệnh được gửi (hoặc, nếu không thể gửi ngay lập tức, đã được xếp hàng), cho phép máy khách sử dụng luồng đó để thực hiện công việc hữu ích khác trong thời gian trung bình .

Có vẻ như tương đương với những gì tôi đã mô tả ở trên bằng cách sử dụng ổ cắm .NET. Có thể có những cách khác để cải thiện hiệu suất khi cố gắng thực hiện nhiều lần gửi, nhưng tôi sẽ bắt đầu ở đây hoặc với bất kỳ đề xuất nào khác được liệt kê trên trang đó. Bạn đã rất mơ hồ về thiết kế ứng dụng của bạn, vì vậy tôi có thể cụ thể hơn tôi đã ở trên. Chỉ cần nhớ, không sử dụng nhiều luồng hơn mức cần thiết để có được những gì bạn cần, nếu không bạn sẽ thấy ứng dụng của mình chạy chậm hơn bạn muốn.

Một số ví dụ trong mã giả (đã cố gắng làm cho nó càng gần băng càng tốt mà tôi không thực sự phải học nó):

var iterations = 100000;
for (int i = 0; i < iterations; i++)
{
    // The thread blocks here waiting for the response.
    // That slows down your loop and you're just wasting
    // CPU cycles that could instead be sending/receiving more objects
    MyObjectPrx obj = iceComm.stringToProxy("whateverissupposedtogohere");
    obj.DoStuff();
}

Một cách tốt hơn:

public interface MyObjectPrx : Ice.ObjectPrx
{
    Ice.AsyncResult GetObject(int obj, Ice.AsyncCallback cb, object cookie);
    // other functions
}

public static void Finished(Ice.AsyncResult result)
{
    MyObjectPrx obj = (MyObjectPrx)result.GetProxy();
    obj.DoStuff();
}

static void Main(string[] args)
{
    // threaded code...
    var iterations = 100000;
    for (int i = 0; i < iterations; i++)
    {
        int num = //whatever
        MyObjectPrx prx = //whatever
        Ice.AsyncCallback cb = new Ice.AsyncCallback(Finished);
        // This function immediately gets called, and the loop continues
        // it doesn't wait for a response, it just continually sends out socket
        // requests as fast as your CPU can handle them.  The response from the
        // server will be handled in the callback function when the request
        // completes.  Hopefully you can see how this is much faster when 
        // sending sockets.  If your server does not use an Async model 
        // like this, however, it's quite possible that your server won't 
        // be able to handle the requests
        prx.GetObject(num, cb, null);
    }
}

Hãy nhớ rằng nhiều chủ đề hơn! = Hiệu suất tốt hơn khi cố gắng gửi ổ cắm (hoặc thực sự làm bất cứ điều gì). Chủ đề không phải là phép thuật ở chỗ chúng sẽ tự động giải quyết bất kỳ vấn đề nào bạn đang làm việc. Lý tưởng nhất là bạn muốn có 1 luồng trên mỗi lõi, trừ khi một luồng dành nhiều thời gian chờ đợi, thì bạn có thể biện minh là có nhiều hơn. Chạy mỗi yêu cầu trong luồng riêng của nó là một ý tưởng tồi, vì các chuyển đổi ngữ cảnh sẽ xảy ra và lãng phí tài nguyên. (Nếu bạn muốn xem mọi thứ tôi đã viết về điều đó, hãy nhấp vào chỉnh sửa và xem các bản sửa đổi trước đây của bài đăng này. Tôi đã xóa nó vì nó dường như chỉ che mờ vấn đề chính trong tay.)

Bạn chắc chắn có thể thực hiện các yêu cầu này trong các luồng, nếu bạn muốn thực hiện một số lượng lớn yêu cầu mỗi giây. Tuy nhiên, đừng quá nhiệt tình với việc tạo chủ đề. Tìm một sự cân bằng và gắn bó với nó. Bạn sẽ có hiệu suất tốt hơn nếu bạn sử dụng mô hình không đồng bộ so với mô hình đồng bộ.

Tôi hy vọng điều đó sẽ giúp.


Tại sao bạn nói về hiệu suất rất nhiều? Đó dường như không phải là điều OP muốn.
Svick

@svick tốt, bài viết gốc của ops có 4 câu hỏi ban đầu và họ đã đặt câu hỏi về hiệu suất của các tác vụ song song với các tác vụ, sau đó nó đã được chỉnh sửa và bây giờ chúng quay lại. Vì vậy, phần lớn những gì bạn đọc là kết quả của điều đó. Cuối cùng, mặc dù câu hỏi của anh ta có liên quan đến hiệu suất, vì anh ta có ý tưởng chung chính xác, nhưng rõ ràng là thiếu trong việc thực hiện. Tôi tin rằng câu trả lời nhọn của tôi ở cuối câu trả lời cho câu hỏi mà anh ấy đã không chỉnh sửa.
Christopher Currens

Tôi buộc phải giảm câu hỏi của mình vì họ muốn bỏ phiếu để đóng. Bây giờ có vẻ như, nó hợp lệ ở đây để có chúng. @ChristopherCurrens +1 điểm tốt cho sự khác biệt với luồng đối với tác vụ. Điều đó mở rộng sự hiểu biết của tôi. Nhưng tôi vẫn bế tắc về việc tạo ra một số yêu cầu N mỗi giây là thực sự có thể? Chính xác thì cách tốt nhất để làm điều đó là gì?
Vua

@King - Tôi đoán tôi không rõ ràng như tôi nghĩ. 3-4 đoạn cuối tôi nghĩ sẽ giúp bạn. Tôi đã giả sử bạn đã sử dụng một vòng lặp các loại rồi. Nếu bạn đang làm điều đó, vấn đề là ổ cắm gửi / nhận của bạn đang bị chặn và do đó làm chậm yêu cầu của bạn. Có lẽ tôi sẽ tìm thấy một chút thời gian để đăng một số mã giả ví dụ.
Christopher Currens

Tôi không có vấn đề trong việc thực sự gửi chúng qua ICE. Vấn đề là những gì xác định việc thực hiện sẽ thực sự tạo ra N yêu cầu và điều gì đó có thể nói đúng với số đó, N.
Vua

2

Tôi sẽ bỏ qua câu hỏi 1) và đi thẳng vào # 2, vì đó thường là cách chấp nhận được để thực hiện những gì bạn đang tìm kiếm. Trong quá khứ để đạt được n tin nhắn mỗi giây, bạn có thể tạo một quy trình duy nhất sau đó sẽ khởi chạy p AppDomains. Mỗi AppDomain về cơ bản chỉ bắt đầu chạy một vòng yêu cầu khi đã đạt đến một thời điểm nhất định (sử dụng Timer). Thời gian này phải giống nhau cho mỗi AppDomain để đảm bảo rằng chúng bắt đầu tấn công máy chủ của bạn tại cùng một thời điểm.

Một cái gì đó như thế này sẽ hoạt động để gửi yêu cầu của bạn:

WaitCallback del = state => 
{ 
    ManualResetEvent[] resetEvents = new ManualResetEvent[10000]; 
    WebClient[] clients = new WebClient[10000]; 

    for (int index = 0; index < 10000; index++) 
    { 
        resetEvents[index] = new ManualResetEvent(false); 
        clients[index] = new WebClient(); 

        clients[index].OpenReadCompleted += new OpenReadCompletedEventHandler (client_OpenReadCompleted); 

        clients[index].OpenReadAsync(new Uri(@"<REQUESTURL>"), resetEvents[index]); 
    } 

    bool succeeded = ManualResetEvent.WaitAll(resetEvents, 10000); 
    Complete(succeeded); 

    for (int index = 0; index < 10000; index++) 
    { 
        resetEvents[index].Dispose(); 
        clients[index].Dispose(); 
    } 
}; 

while(running)
{
    ThreadPool.QueueUserWorkItem(del);
    Thread.Sleep(1000);
}

Điều này có thể sẽ phá vỡ hiệu suất trên bất kỳ máy nào bạn đang chạy, vì vậy bạn luôn có thể thực hiện một loại vòng lặp tương tự từ một số máy khác nhau nếu bạn có tài nguyên (sử dụng các quy trình thay vì miền ứng dụng).

Đối với câu hỏi thứ ba của bạn, hãy cho liên kết này đọc http://www.albahari.com/threading/

Cuối cùng, một chiếc đồng hồ bấm giờ nên được ghép nối với bộ đếm lần truy cập để theo dõi cả thời lượng và số lần truy cập duy nhất trên máy chủ của bạn. Điều đó sẽ cho phép bạn thực hiện một số phân tích sau khi thực tế.


2
Lý do nào có thể bạn sẽ phải tạo các AppDomain riêng ở đây? Điều đó dường như hoàn toàn không cần thiết.
Svick

0

Đừng bận tâm với các chủ đề, nếu N là nhỏ hợp lý. Để tạo N yêu cầu mỗi giây, hãy sử dụng thời gian đồng hồ treo tường ( DateTime.Now). Dành thời gian cả trước và sau khi yêu cầu, sau đó thêm một Sleepđể trì hoãn yêu cầu tiếp theo.

Chẳng hạn, với N = 5 (200 ms):

Before request: 12:33:05.014
After request: 12:33:05.077
Sleep(137)
Before request: 12:33:05.214
After request: 12:33:05.271
Sleep(131)

Điều này không hoàn hảo; bạn có thể thấy điều đó Sleepkhông chính xác. Bạn có thể giữ số lần sai lệch đang chạy (trước các yêu cầu X'th, thời gian sẽ là X-1 / N sau) và điều chỉnh thời gian Ngủ phù hợp.

Khi N trở nên quá lớn, bạn chỉ cần tạo các luồng M và để mỗi luồng tạo các yêu cầu N / M theo cùng một kiểu.


Tôi phải tạo ra số lượng yêu cầu rất cao. Vì vậy, đây không thể là tùy chọn vì nó sẽ uống bộ nhớ của tôi (RAM 4GB) ngay cả trước 100 luồng.
Vua

Tôi đã tạo 20.000 yêu cầu mỗi giây từ một chuỗi, trong 250K mã. Dù sao bạn cũng không có đủ CPU để chạy 100 luồng (loại máy đó không đi kèm 4GB). Vấn đề tiếp theo sẽ là đẩy tất cả những yêu cầu đó ra; Bạn có Ethernet 10 Gbit / s giữa người tạo tải và máy chủ của bạn không? Vì vậy, bạn có thể muốn kiểm tra các yêu cầu thực tế của bạn.
MSalters

Để làm rõ, tôi có một cái gì đó như 20+ Gbps. Vì vậy, đó không phải là một vấn đề. Về lớp máy móc, bạn sẽ đề cập đến điều gì? số lượng bộ xử lý?
Vua

@King: để đẩy 100 luồng tôi mong đợi một máy 48 lõi. Chẳng hạn, SGI bán các máy có nhiều lõi như vậy, nhưng trên các máy bạn thường nhận được 32 GB trở lên.
MSalters

0

Cách dễ nhất để đạt được kiểm tra tải cho bất kỳ dự án .NET nào là mua phiên bản Ultimate của Visual Studio. Điều này đi kèm với một công cụ kiểm tra tích hợp để giúp tạo ra tất cả các loại thử nghiệm bao gồm cả thử nghiệm tải. Tải thử nghiệm có thể được tạo trước bằng cách tạo người dùng ảo trên một PC hoặc được phân phối trên một số lượng lớn người dùng, cũng có một chương trình nhỏ có thể được cài đặt trên các máy chủ mục tiêu để trả về dữ liệu bổ sung trong thời gian thử nghiệm.

Điều này là đắt tiền mặc dù, nhưng phiên bản cuối cùng đi kèm với rất nhiều tính năng, vì vậy nếu tất cả được sử dụng, nó sẽ là một mức giá hợp lý hơn.


0

Nếu bạn hoàn toàn muốn có tất cả các luồng X đánh vào tài nguyên của mình cùng một lúc, bạn có thể đặt từng luồng phía sau chốt đếm ngược và chỉ định khoảng thời gian chờ ngắn giữa các lần kiểm tra semaphore.

C # có một triển khai (http://msdn.microsoft.com/en-us/l Library / system.threading.countdownevent (VS.100) .aspx).

Đồng thời, nếu bạn đang căng thẳng kiểm tra hệ thống của mình, bạn thực sự có thể muốn kiểm tra điều kiện cuộc đua, trong trường hợp đó bạn sẽ muốn thiết lập thời gian ngủ của luồng trên mỗi luồng theo thời gian với tần suất ngẫu nhiên và đỉnh / lông thú.

Tương tự như vậy, bạn có thể không thực sự muốn nhanh chóng gửi nhiều yêu cầu, bạn có thể thành công hơn khi đưa máy chủ của mình vào trạng thái xấu / kiểm tra hiệu suất thế giới thực của nó bằng cách thiết lập một số lượng nhỏ hơn các chủ đề tốn nhiều thời gian hơn trong việc tiêu thụ và gửi lại tin nhắn và qua ổ cắm, vì máy chủ của bạn có thể sẽ cần phải quay các luồng của chính nó để xử lý các tin nhắn chậm đang diễn ra.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.