I need to execute strategy.AllTablesUpdated();
for 50 strategies in 2 ms (and I need to repeat that ~500 times per second).
Using code below I discovered that just Monitor.TryEnter
call spents up to 1 ms (!!!) and I do that 50 times!
// must be called ~500 times per second
public void FinishUpdatingTables()
{
foreach (Strategy strategy in strategies) // about ~50, should be executed in 2 ms
{
// this slow and can be paralleled
strategy.AllTablesUpdated();
}
}
...................
public override bool AllTablesUpdated(Stopwatch sw)
{
this.sw = sw;
Checkpoint(this + " TryEnter attempt ");
if (Monitor.TryEnter(desiredOrdersBuy))
{
Checkpoint(this + " TryEnter success ");
try
{
OnAllTablesUpdated();
} finally
{
Monitor.Exit(desiredOrdersBuy);
}
return true;
} else
{
Checkpoint(this + " TryEnter failed ");
}
return false;
}
public void Checkpoint(string message)
{
if (sw == null)
{
return;
}
long time = sw.ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
Log.Push(LogItemType.Debug, message + time);
}
From logs (in µs), failed attempt spents ~ 1ms:
12:55:43:778 Debug: TryEnter attempt 1264 12:55:43:779 Debug: TryEnter failed 2123
From logs (in µs), succeed attempt spents ~ 0.01ms:
12:55:49:701 Debug: TryEnter attempt 889 12:55:49:701 Debug: TryEnter success 900
So now I think that Monitor.TryEnter
is too expensive for me to be executed one by one for 50 strategies. So I want to parallel this work using Task
like that:
// must be called ~500 times per second
public void FinishUpdatingTables()
{
foreach (Strategy strategy in strategies) // about ~50, should be executed in 2 ms
{
// this slow and can be paralleled
Task.Factory.StartNew(() => {
strategy.AllTablesUpdated();
});
}
}
I will also probably replace Monitor.TryEnter
to just lock
as with such approach everything will be asynchronous.
My questions:
- Why
Monitor.TryEnter
is so slow ? (1 ms if lock is not obtained) - How good would be to start 50
Task
each 2 ms = 25 000 of Tasks each second? Can .NET manage this effectively? I can also use producer-consumer pattern withBlockingCollection
and start 50 "workers" only ONCE and then submit new pack of 50 items each 2 ms to BlockingCollection? Would that be better? - How would you execute 50 methods that can be paralleled each 2 ms (500 times per second), totally 25 000 times per second?