Cost of ExecutionContext.Capture () and ExecutionContext.Run (context, operation, state)

Does anyone know if this is expensive ExecutionContext.Capture() and ExecutionContext.Run(context, work, state) ?

Does performance decrease and therefore it is recommended to use it carefully?

I ask, since I have a ContextItem where I save the Context work and enable it for execution later. Since I want to be able to respond to an exception that can be thrown during the execution of the work, I have a reserve that is executed if the exception is thrown from the work. And I also have the final work, which is done anyway regardless of whether the exception was thrown. Since I can only use ExecutionContext once, I would have to ExecutionContext.Capture() three times for one of these ContextItems ...

Or does this sound like a completely wrong approach?

+6
source share
1 answer

As recommended by @Alois Kraus, I checked the test with the following code comparing a lock with a capture and aligned executor:

 class Program { private static readonly object _lock = new object(); private static readonly int numberOfItems = 1000000; private static readonly int _numberOfIterations = 1000000; private static void Main(string[] args) { MeasureTimeWithLocking(); MeasureTimeWithCapuringContext(); Console.WriteLine(); MeasureTimeWithLocking(); MeasureTimeWithCapuringContext(); Console.WriteLine(); MeasureTimeWithLocking(); MeasureTimeWithCapuringContext(); Console.ReadKey(); } private static void MeasureTimeWithLocking() { List<ContextItem> items = new List<ContextItem>(); Stopwatch stopwatch = Stopwatch.StartNew(); for (int i = 0; i < numberOfItems; i++) { ContextItem item = new ContextItem(); item.Work1 = DoSomeWorkWithLock; item.Work2 = DoSomeWorkWithLock; item.Work3 = DoSomeWorkWithLock; } Parallel.ForEach(items, (item) => { item.Work1(null); item.Work2(null); item.Work3(null); }); stopwatch.Stop(); Console.WriteLine("Time elapsed with locking: " + stopwatch.Elapsed); } private static void MeasureTimeWithCapuringContext() { List<ContextItem> items = new List<ContextItem>(); Stopwatch stopwatch = Stopwatch.StartNew(); for (int i = 0; i < numberOfItems; i++) { ContextItem item = new ContextItem(); item.Context1 = ExecutionContext.Capture(); item.Context2 = ExecutionContext.Capture(); item.Context3 = ExecutionContext.Capture(); item.Work1 = DoSomeWork; item.Work2 = DoSomeWork; item.Work3 = DoSomeWork; } foreach (ContextItem item in items) { ExecutionContext.Run(item.Context1, item.Work1, null); ExecutionContext.Run(item.Context2, item.Work2, null); ExecutionContext.Run(item.Context3, item.Work3, null); } stopwatch.Stop(); Console.WriteLine("Time elapsed with capturing context: " + stopwatch.Elapsed); } private static void DoSomeWork(object ignored) { Work(); } private static void DoSomeWorkWithLock(object ignored) { lock (_lock) { Work(); } } private static void Work() { int count = 0; for (int i = 0; i < _numberOfIterations; i++) { count ++; } } private class ContextItem { public ExecutionContext Context1 { get; set; } public ExecutionContext Context2 { get; set; } public ExecutionContext Context3 { get; set; } public ContextCallback Work1 { get; set; } public ContextCallback Work2 { get; set; } public ContextCallback Work3 { get; set; } } } 

Results:

enter image description here

So, if I did it right, capture and execution lined up on average about 5 times more expensive than locking.

To answer part of my question:

Or does this sound like a completely wrong approach?

I read in this article that

if you need to know that they are there, either you are doing something super advanced, or something went wrong.

The article was recommended for SO as the best source if you want to learn about ExecutionContext. After going through it and checking a few tests with a colleague, I realized that I am using ExecutionContext where it does not make sense, plus it is less efficient than locking, and therefore it is probably also less efficient than other stream functions / constructs.

+1
source

All Articles