使用等待Task.Delay在为杀死性能 [英] using await Task.Delay in a for kills performance
问题描述
比方说,我要开始每秒均匀分布的大约N个任务。
Let's say I want to start roughly N tasks per second distributed equally.
所以,我想这样的:
public async Task Generate(int numberOfCallsPerSecond)
{
var delay = TimeSpan.FromMiliseconds(1000/numberOfCallsPerSecond); // a call should happen every 1000 / numberOfCallsPerSecond miliseconds
for (int i=0; i < numberOfcallsPerSecond; i++)
{
Task t = Call(); // don't wait for result here
await Task.Delay(delay);
}
}
起初我预计这1秒钟内运行,但为 numberOfCallsPerSecond = 100
需要16秒
我12核CPU。
看来的await Task.Delay增添了不少的开销(当然不到位产生的话费发生在3ms的。
At first I expected this to run in 1 second but for numberOfCallsPerSecond = 100
it takes 16 seconds
on my 12 core CPU.
It seems the await Task.Delay adds a lot of overhead (of course without it in place generation of the calls happens in 3ms.
我没有?预计等待将在此情况下加那么多的开销这是正常的。
I didn't expect that await would add so much overhead in this scenario. Is this normal?
编辑:
请忘记关于()的调用运行这段代码显示similiar结果:
Please forget about the Call(). Running this code shows similiar result:
public async Task Generate(int numberOfCallsPerSecond)
{
var delay = TimeSpan.FromMiliseconds(1000/numberOfCallsPerSecond); // a call should happen every 1000 / numberOfCallsPerSecond miliseconds
for (int i=0; i < numberOfcallsPerSecond; i++)
{
await Task.Delay(delay);
}
}
我试图用 numberOfCallsPerSecond = 500
,它需要几秒钟大约10时,我以为生成
来大约需要1第二个,不是10倍以上。
I tried to run it with numberOfCallsPerSecond = 500
and it takes around 10 seconds, I expected Generate
to take roughly 1 second, not 10 times more
推荐答案
Task.Delay
是轻量级的,但不准确的。由于无延迟的循环完成得更快,这听起来像你的线程会闲置和使用OS的睡眠等待计时器的等待。计时器根据操作系统线程调度量子(在相同的中断处理它执行线程占先),这是16ms的默认选中
Task.Delay
is lightweight but not accurate. Since the loop without delay completes much faster, it sounds like your thread is going idle and using an OS sleep to wait for the timer to elapse. The timer is checked according to the OS thread scheduling quantum (in the same interrupt handler which performs thread pre-emption), which is 16ms by default.
可以减少与 timeBeginPeriod
量子,但更好的(更节能)的方法,如果你需要的速率限制,而不是确切的时间是保持的经过时间的轨道(即秒表
类是唯一的好本),并提出呼叫次数和延迟通话时已经赶上经过时间的。整体效果是,你的线程会醒来每秒〜60倍,并开始几个工作项各它的时间。如果你的CPU变成忙于别的事情,你会开始,当你得到控制权交还给额外的工作项目 - 尽管它也非常简单封顶的项目数量立刻开始,如果这就是你所需要的
You can reduce the quantum with timeBeginPeriod
, but a better (more power efficient) approach if you need rate limiting rather than exact timing is to keep track of elapsed time (the Stopwatch
class is good for this) and number of calls made, and only delay when calls made have caught up to elapsed time. The overall effect is that your thread will wake up ~60 times per second, and start a few work items each time it does. If your CPU becomes busy with something else, you'll start extra work items when you get control back -- although it's also pretty straightforward to cap the number of items started at once, if that's what you need.
public async Task Generate(int numberOfCallsPerSecond)
{
var elapsed = Stopwatch.StartNew();
var delay = TimeSpan.FromMiliseconds(1000/numberOfCallsPerSecond); // a call should happen every 1000 / numberOfCallsPerSecond miliseconds
for (int i=0; i < numberOfcallsPerSecond; i++)
{
Call(); // don't wait for result here
int expectedI = elapsed.Elapsed.TotalSeconds * numberOfCallsPerSecond;
if (i > expectedI) await Task.Delay(delay);
}
}
这篇关于使用等待Task.Delay在为杀死性能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!