简体   繁体   English

在处理大量数据时减少CPU使用率

[英]Reduce CPU usage while processing large amount of data

I am writing a real time application which receives around 2000 messages per second which was pushed in a queue. 我正在编写一个实时应用程序,该应用程序每秒接收大约2000条消息,并排入队列。 I have written a background thread which process the messages in the queue. 我编写了一个后台线程来处理队列中的消息。

private void ProcessSocketMessage()
{
    while (!this.shouldStopProcessing)
    {
        while (this.messageQueue.Count > 0)
        {
            string message;
            bool result = this.messageQueue.TryDequeue(out message);
            if (result)
            {
               // Process the string and do some other stuff
               // Like updating the received message in a datagrid  
            }
        }
    }
}

The problem with the above code is that it uses insane amount of processing power around 12% of CPU(2.40 GHz dual core processor). 上面的代码的问题在于,它消耗了大约12%的CPU(2.40 GHz双核处理器)的疯狂处理能力。

I have 4 blocks similar to the one above which literally takes up 50 % of CPU computing power. 我有4个类似于上面的块,这些块实际上占用了CPU计算能力的50%。 Is there anything which can be optimized in the above code? 上面的代码中有什么可以优化的吗?

Adding a Thread Sleep of 100 ms before second while loop end does seems to be increase the performance by 50%. 在第二次while循环结束之前添加100 ms的线程休眠似乎可以将性能提高50%。 But am I doing something wrong? 但是我做错了吗?

This functionality is already provided in the Dataflow library's ActionBlock class. Dataflow库的 ActionBlock类中已经提供了此功能。 An ActionBlock has an input buffer that receives messages and processes them by calling an action for each one. 一个ActionBlock具有一个输入缓冲区,该缓冲区接收消息并通过为每个消息调用一个动作来处理消息。 By default, only one message is processed at a time. 默认情况下,一次只处理一封邮件。 It doesn't use busy waiting. 它不需要忙碌的等待。

void MyActualProcessingMethod(string it)
{
  // Process the string and do some other stuff
}

var myBlock = new ActionBlock<string>( someString =>MyActualProcessingMethod(someString));

//Simulate a lot of messages
for(int i=0;i<100000;i++)
{
    myBlock.Post(someMessage);
}

When the messages finish and/or we don't want any more messages, we command it to complete, by refusing any new messages and processing anything left in the input buffer: 当消息完成和/或我们不再需要任何消息时,我们通过拒绝任何新消息并处理输入缓冲区中剩下的任何内容来命令它完成:

myBlock.Complete();

Before we finish, we need to actually await for the block to finish processing the leftovers: 在完成之前,我们需要实际等待该块完成剩余的处理:

await myBlock.Completion;

All Dataflow blocks can accept messages from multiple clients. 所有Dataflow块都可以接受来自多个客户端的消息。

Blocks can be combined as well. 块也可以组合。 The output of one block can feed another. 一个块的输出可以提供给另一块。 The TransformBlock accepts a function that transforms an input into an output. TransformBlock接受将输入转换为输出的功能。

Typically each block uses tasks from the thread pool. 通常,每个块都使用线程池中的任务。 By default one block processes only one message at a time. 默认情况下,一个块一次仅处理一条消息。 Different blocks run on different tasks or even different TaskSchedulers. 不同的块在不同的任务甚至不同的TaskScheduler上运行。 This way, you can have one block do some heavy processing and push a result to another block that updates the UI. 这样,您可以让一个块进行一些繁重的处理,然后将结果推送到另一个更新UI的块中。

string MyActualProcessingMethod(string it)
{
  // Process the string and do some other stuff
  // and send a progress message downstream
  return SomeProgressMessage;
}

void UpdateTheUI(string msg)
{
  statusBar1.Text = msg;
}

var myProcessingBlock = new TransformBlock<string,string>(msg =>MyActualProcessingMethod(msg));

The UI will be updated by another block that runs on the UI thread. UI将由在UI线程上运行的另一个块更新。 This is expressed through the ExecutionDataflowBlockOptions : 这通过ExecutionDataflowBlockOptions表示:

var runOnUI=new ExecutionDataflowBlockOptions { 
                  TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext() 
            };
var myUpdater = new ActionBlock<string>(msg => UpdateTheUI(msg),runOnUI);

//Pass progress messages from the processor to the updater
myProcessingBlock.LinkTo(myUpdater,new DataflowLinkOptions { PropagateCompletion = true });

The code that posts messages to the pipeline's first block doesn't change : 将消息发布到管道的第一个块的代码不变:

//Simulate a lot of messages
for(int i=0;i<100000;i++)
{
    myProcessingBlock.Post(someMessage);
}


//We are finished, tell the block to process any leftover messages
myProcessingBlock.Complete();

In this case, as soon as the procesor completes it will notify the next block in the pipeline to complete. 在这种情况下,一旦处理器完成,它将通知管道中的下一个块完成。 We need to wait for that final block to complete as well 我们也需要等待最后一步完成

//Wait for the block to finish
await myUpdater.Completion;

How about making the first block work in parallel? 如何使第一个块并行工作? We can specify that up to eg 10 tasks will be used to process input messages through its execution options : 我们可以指定多达10个任务将通过其执行选项来处理输入消息:

var dopOptions = new ExecutionDataflowBlockOptions {MaxDegreeOfParallelism = 10};
var myProcessingBlock = new TransformBlock<string,string>(msg =>MyActualProcessingMethod(msg),dopOptions);

The processor will process up to 10 messages in parallel but the updater will still process them one by one, in the UI thread. 处理器将并行处理多达10条消息,但更新程序仍将在UI线程中一一处理它们。

You're best bet is to use a profile to monitor the running application and determine for sure where the CPU is spending it's time. 最好的选择是使用配置文件来监视正在运行的应用程序,并确定CPU在哪里花时间。

However, it looks like you have the possibility for a busy-wait loop if this.messageQueue.Count is 0. At minimum, I would suggest adding a small pause if the queue is empty to allow a message to go onto the queue. 但是,如果this.messageQueue.Count为0,则看起来您可能有一个忙等待循环。至少,如果队列为空,我建议添加一个小暂停以允许消息进入队列。 Otherwise your CPU is just spending time checking the queue over and over and over. 否则,您的CPU只会花时间反复检查队列。

If the time is spent dequeueing messages, you may want to consider handling multiple messages at once (if there are multiple messages available), assuming you're queue allows you to pop multiple messages off the queue in a single call. 如果花费时间使消息出队,则可能要考虑一次处理多条消息(如果有多条消息可用),假设您正在排队,则允许您在单个呼叫中将多条消息弹出队列。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM