SlideShare ist ein Scribd-Unternehmen logo
1 von 58
Downloaden Sie, um offline zu lesen
BEYOND SIMPLE BENCHMARKS
A PRACTICAL GUIDE TO OPTIMIZING CODE
 | ✉ | 
danielmarbach daniel.marbach@particular.net Daniel Marbach
"SIMPLE"
Microsoft Teams’ Infrastructure and Azure Communication Services’ Journey to .NET 6
"We were able to see Azure Compute cost reduction of up to
50% per month, on average we observed 24% monthly cost
reduction after migrating to .NET 6. The reduction in cores
reduced Azure spend by 24%."
PERFORMANCE AWARE
BEAR AWARE
BE CURIOUS....
UNDERSTAND THE CONTEXT
How is this code going to be executed at
scale, and what would the memory
characteristics be (gut feeling)
Are there simple low-hanging fruits I can
apply to accelerate this code?
Are there things I can move away from the
hot path by simply restructuring a bit my
code?
What part is under my control and what isn't
really?
What optimizations can I apply, and when
should I stop?
THE PERFORMANCE LOOP
Profile at least CPU and memory using
a profiling harness
Improve parts of the hot path
Benchmark and compare
Profile improvements again with the
harness and make adjustments where
necessary
Ship and focus your attention to other
parts
Queue Code
NSERVICEBUS
go.particular.net/webinar-2023-quickstart
Queue
Message Pump
Behaviors
Code
...
NSERVICEBUS PIPELINE
ASP.NET CORE MIDDLEWARE
public class RequestCultureMiddleware {
_next = next;
public async Task InvokeAsync(HttpContext context) {
await _next(context);
1
private readonly RequestDelegate _next;
2
3
public RequestCultureMiddleware(RequestDelegate next) {
4
5
}
6
7
8
// Do work that does something before
9
10
// Do work that does something after
11
}
12
}
13
public class Behavior : Behavior<IIncomingLogicalMessageContext> {
public override Task
Invoke(IIncomingLogicalMessageContext context, Func<Task> next) {
await next();
1
2
3
// Do work that does something before
4
5
// Do work that does something after
6
}
7
}
8
BEHAVIORS
PROFILING THE PIPELINE
THE HARNESS
Compiled and executed in Release mode
Runs a few seconds and keeps overhead
minimal
Disabled Tiered JIT
<TieredCompilation>false</TieredCompilation>
Emits full symbols
<DebugType>pdbonly</DebugType
<DebugSymbols>true</DebugSymbols>
var endpointConfiguration = new EndpointConfiguration("PublishSample");
endpointConfiguration.UseSerialization<JsonSerializer>();
var transport = endpointConfiguration.UseTransport<MsmqTransport>();
transport.Routing().RegisterPublisher(typeof(MyEvent), "PublishSample");
endpointConfiguration.UsePersistence<InMemoryPersistence>();
endpointConfiguration.EnableInstallers();
endpointConfiguration.SendFailedMessagesTo("error");
var endpointInstance = await Endpoint.Start(endpointConfiguration);
Console.WriteLine("Attach the profiler and hit <enter>.");
Console.ReadLine();
1
2
3
4
5
6
7
8
9
10
11
12
13
var tasks = new List<Task>(1000);
14
for (int i = 0; i < 1000; i++)
15
{
16
tasks.Add(endpointInstance.Publish(new MyEvent()));
17
}
18
await Task.WhenAll(tasks);
19
20
Console.WriteLine("Publish 1000 done. Get a snapshot");
21
Console.ReadLine();
22
var transport = endpointConfiguration.UseTransport<MsmqTransport>();
var endpointConfiguration = new EndpointConfiguration("PublishSample");
1
endpointConfiguration.UseSerialization<JsonSerializer>();
2
3
transport.Routing().RegisterPublisher(typeof(MyEvent), "PublishSample");
4
endpointConfiguration.UsePersistence<InMemoryPersistence>();
5
endpointConfiguration.EnableInstallers();
6
endpointConfiguration.SendFailedMessagesTo("error");
7
8
var endpointInstance = await Endpoint.Start(endpointConfiguration);
9
10
Console.WriteLine("Attach the profiler and hit <enter>.");
11
Console.ReadLine();
12
13
var tasks = new List<Task>(1000);
14
for (int i = 0; i < 1000; i++)
15
{
16
tasks.Add(endpointInstance.Publish(new MyEvent()));
17
}
18
await Task.WhenAll(tasks);
19
20
Console.WriteLine("Publish 1000 done. Get a snapshot");
21
Console.ReadLine();
22
endpointConfiguration.UseSerialization<JsonSerializer>();
var endpointConfiguration = new EndpointConfiguration("PublishSample");
1
2
var transport = endpointConfiguration.UseTransport<MsmqTransport>();
3
transport.Routing().RegisterPublisher(typeof(MyEvent), "PublishSample");
4
endpointConfiguration.UsePersistence<InMemoryPersistence>();
5
endpointConfiguration.EnableInstallers();
6
endpointConfiguration.SendFailedMessagesTo("error");
7
8
var endpointInstance = await Endpoint.Start(endpointConfiguration);
9
10
Console.WriteLine("Attach the profiler and hit <enter>.");
11
Console.ReadLine();
12
13
var tasks = new List<Task>(1000);
14
for (int i = 0; i < 1000; i++)
15
{
16
tasks.Add(endpointInstance.Publish(new MyEvent()));
17
}
18
await Task.WhenAll(tasks);
19
20
Console.WriteLine("Publish 1000 done. Get a snapshot");
21
Console.ReadLine();
22
endpointConfiguration.UsePersistence<InMemoryPersistence>();
var endpointConfiguration = new EndpointConfiguration("PublishSample");
1
endpointConfiguration.UseSerialization<JsonSerializer>();
2
var transport = endpointConfiguration.UseTransport<MsmqTransport>();
3
transport.Routing().RegisterPublisher(typeof(MyEvent), "PublishSample");
4
5
endpointConfiguration.EnableInstallers();
6
endpointConfiguration.SendFailedMessagesTo("error");
7
8
var endpointInstance = await Endpoint.Start(endpointConfiguration);
9
10
Console.WriteLine("Attach the profiler and hit <enter>.");
11
Console.ReadLine();
12
13
var tasks = new List<Task>(1000);
14
for (int i = 0; i < 1000; i++)
15
{
16
tasks.Add(endpointInstance.Publish(new MyEvent()));
17
}
18
await Task.WhenAll(tasks);
19
20
Console.WriteLine("Publish 1000 done. Get a snapshot");
21
Console.ReadLine();
22
var tasks = new List<Task>(1000);
for (int i = 0; i < 1000; i++)
{
tasks.Add(endpointInstance.Publish(new MyEvent()));
}
await Task.WhenAll(tasks);
var endpointConfiguration = new EndpointConfiguration("PublishSample");
1
endpointConfiguration.UseSerialization<JsonSerializer>();
2
var transport = endpointConfiguration.UseTransport<MsmqTransport>();
3
transport.Routing().RegisterPublisher(typeof(MyEvent), "PublishSample");
4
endpointConfiguration.UsePersistence<InMemoryPersistence>();
5
endpointConfiguration.EnableInstallers();
6
endpointConfiguration.SendFailedMessagesTo("error");
7
8
var endpointInstance = await Endpoint.Start(endpointConfiguration);
9
10
Console.WriteLine("Attach the profiler and hit <enter>.");
11
Console.ReadLine();
12
13
14
15
16
17
18
19
20
Console.WriteLine("Publish 1000 done. Get a snapshot");
21
Console.ReadLine();
22
var endpointConfiguration = new EndpointConfiguration("PublishSample");
endpointConfiguration.UseSerialization<JsonSerializer>();
var transport = endpointConfiguration.UseTransport<MsmqTransport>();
transport.Routing().RegisterPublisher(typeof(MyEvent), "PublishSample");
endpointConfiguration.UsePersistence<InMemoryPersistence>();
endpointConfiguration.EnableInstallers();
endpointConfiguration.SendFailedMessagesTo("error");
var endpointInstance = await Endpoint.Start(endpointConfiguration);
Console.WriteLine("Attach the profiler and hit <enter>.");
Console.ReadLine();
var tasks = new List<Task>(1000);
for (int i = 0; i < 1000; i++)
{
tasks.Add(endpointInstance.Publish(new MyEvent()));
}
await Task.WhenAll(tasks);
Console.WriteLine("Publish 1000 done. Get a snapshot");
Console.ReadLine();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MyEventHandler : IHandleMessages<MyEvent> {
public Task Handle(MyEvent message, IMessageHandlerContext context)
{
Console.WriteLine("Event received");
return Task.CompletedTask;
}
}
 Profiling the pipeline
PUBLISH
MEMORY CHARACTERISTICS
 Profiling the pipeline
RECEIVE
MEMORY CHARACTERISTICS
 Profiling the pipeline
BEHAVIORCHAIN
MEMORY CHARACTERISTICS
 Profiling the pipeline
CONTEXT MATTERS
MEMORY CHARACTERISTICS
 Profiling the pipeline
MEMORY CHARACTERISTICS
 Profiling the pipeline
MEMORY CHARACTERISTICS
 Profiling the pipeline
CPU CHARACTERISTICS
 Profiling the pipeline
CPU CHARACTERISTICS
 Profiling the pipeline
CPU CHARACTERISTICS
PUBLISH
 Profiling the pipeline
CPU CHARACTERISTICS
RECEIVE
 Profiling the pipeline
CPU CHARACTERISTICS
 Profiling the pipeline
TESTING
 10X faster execution with
compiled expression trees
 How we achieved 5X faster
pipeline execution by removing
closure allocations
IMPROVING
go.particular.net/webinar-2023-pipeline
BENCHMARKING THE PIPELINE
 Benchmarking the pipeline
Copy and paste relevant code
Adjust it to the bare essentials to create a
controllable environment
EXTRACT CODE
Trim down to relevant behaviors
Replaced dependency injection container
with creating relevant classes
Replaced IO-operations with completed tasks
EXTRACT CODE
 Benchmarking the pipeline
Get started with small steps
Culture change takes time
Make changes gradually
PERFORMANCE CULTURE
[ShortRunJob]
[MemoryDiagnoser]
public class PipelineExecution {
[Params(10, 20, 40)]
public int PipelineDepth { get; set; }
[GlobalSetup]
public void SetUp() {
behaviorContext = new BehaviorContext();
pipelineModificationsBeforeOptimizations = new PipelineModifications();
for (int i = 0; i < PipelineDepth; i++)
{
pipelineModificationsBeforeOptimizations.Additions.Add(RegisterStep.Create(i.ToString(),
typeof(BaseLineBehavior), i.ToString(), b => new BaseLineBehavior()));
}
pipelineModificationsAfterOptimizations = new PipelineModifications();
for (int i = 0; i < PipelineDepth; i++)
{
pipelineModificationsAfterOptimizations.Additions.Add(RegisterStep.Create(i.ToString(),
typeof(BehaviorOptimization), i.ToString(), b => new BehaviorOptimization()));
}
pipelineBeforeOptimizations = new BaseLinePipeline<IBehaviorContext>(null, new SettingsHolder(),
pipelineModificationsBeforeOptimizations);
pipelineAfterOptimizations = new PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(),
pipelineModificationsAfterOptimizations);
}
[Benchmark(Baseline = true)]
public async Task Before() {
await pipelineBeforeOptimizations.Invoke(behaviorContext);
}
[Benchmark]
public async Task After() {
await pipelineAfterOptimizations.Invoke(behaviorContext);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
 Benchmarking the pipeline
[GlobalSetup]
public void SetUp() {
behaviorContext = new BehaviorContext();
pipelineModificationsBeforeOptimizations = new PipelineModifications();
for (int i = 0; i < PipelineDepth; i++)
{
pipelineModificationsBeforeOptimizations.Additions.Add(RegisterStep.Create(i.ToString(),
typeof(BaseLineBehavior), i.ToString(), b => new BaseLineBehavior()));
}
pipelineModificationsAfterOptimizations = new PipelineModifications();
for (int i = 0; i < PipelineDepth; i++)
{
pipelineModificationsAfterOptimizations.Additions.Add(RegisterStep.Create(i.ToString(),
typeof(BehaviorOptimization), i.ToString(), b => new BehaviorOptimization()));
}
pipelineBeforeOptimizations = new BaseLinePipeline<IBehaviorContext>(null, new SettingsHolder(),
pipelineModificationsBeforeOptimizations);
pipelineAfterOptimizations = new PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(),
pipelineModificationsAfterOptimizations);
}
[ShortRunJob]
1
[MemoryDiagnoser]
2
public class PipelineExecution {
3
4
[Params(10, 20, 40)]
5
public int PipelineDepth { get; set; }
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
[Benchmark(Baseline = true)]
33
public async Task Before() {
34
await pipelineBeforeOptimizations.Invoke(behaviorContext);
35
}
36
37
[Benchmark]
38
public async Task After() {
39
await pipelineAfterOptimizations.Invoke(behaviorContext);
40
}
41
}
42
 Benchmarking the pipeline
[Params(10, 20, 40)]
public int PipelineDepth { get; set; }
for (int i = 0; i < PipelineDepth; i++)
for (int i = 0; i < PipelineDepth; i++)
[ShortRunJob]
1
[MemoryDiagnoser]
2
public class PipelineExecution {
3
4
5
6
7
8
[GlobalSetup]
9
public void SetUp() {
10
behaviorContext = new BehaviorContext();
11
12
pipelineModificationsBeforeOptimizations = new PipelineModifications();
13
14
{
15
pipelineModificationsBeforeOptimizations.Additions.Add(RegisterStep.Create(i.ToString(),
16
typeof(BaseLineBehavior), i.ToString(), b => new BaseLineBehavior()));
17
}
18
19
pipelineModificationsAfterOptimizations = new PipelineModifications();
20
21
{
22
pipelineModificationsAfterOptimizations.Additions.Add(RegisterStep.Create(i.ToString(),
23
typeof(BehaviorOptimization), i.ToString(), b => new BehaviorOptimization()));
24
}
25
26
pipelineBeforeOptimizations = new BaseLinePipeline<IBehaviorContext>(null, new SettingsHolder(),
27
pipelineModificationsBeforeOptimizations);
28
pipelineAfterOptimizations = new PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(),
29
pipelineModificationsAfterOptimizations);
30
}
31
32
[Benchmark(Baseline = true)]
33
public async Task Before() {
34
await pipelineBeforeOptimizations.Invoke(behaviorContext);
35
}
36
37
[Benchmark]
38
public async Task After() {
39
await pipelineAfterOptimizations.Invoke(behaviorContext);
40
}
41
}
42
[ShortRunJob]
[MemoryDiagnoser]
1
2
public class PipelineExecution {
3
4
[Params(10, 20, 40)]
5
public int PipelineDepth { get; set; }
6
7
8
[GlobalSetup]
9
public void SetUp() {
10
behaviorContext = new BehaviorContext();
11
12
pipelineModificationsBeforeOptimizations = new PipelineModifications();
13
for (int i = 0; i < PipelineDepth; i++)
14
{
15
pipelineModificationsBeforeOptimizations.Additions.Add(RegisterStep.Create(i.ToString(),
16
typeof(BaseLineBehavior), i.ToString(), b => new BaseLineBehavior()));
17
}
18
19
pipelineModificationsAfterOptimizations = new PipelineModifications();
20
for (int i = 0; i < PipelineDepth; i++)
21
{
22
pipelineModificationsAfterOptimizations.Additions.Add(RegisterStep.Create(i.ToString(),
23
typeof(BehaviorOptimization), i.ToString(), b => new BehaviorOptimization()));
24
}
25
26
pipelineBeforeOptimizations = new BaseLinePipeline<IBehaviorContext>(null, new SettingsHolder(),
27
pipelineModificationsBeforeOptimizations);
28
pipelineAfterOptimizations = new PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(),
29
pipelineModificationsAfterOptimizations);
30
}
31
32
[Benchmark(Baseline = true)]
33
public async Task Before() {
34
await pipelineBeforeOptimizations.Invoke(behaviorContext);
35
}
36
37
[Benchmark]
38
public async Task After() {
39
await pipelineAfterOptimizations.Invoke(behaviorContext);
40
}
41
}
42
[Benchmark(Baseline = true)]
public async Task Before() {
await pipelineBeforeOptimizations.Invoke(behaviorContext);
}
[Benchmark]
public async Task After() {
await pipelineAfterOptimizations.Invoke(behaviorContext);
}
[ShortRunJob]
1
[MemoryDiagnoser]
2
public class PipelineExecution {
3
4
[Params(10, 20, 40)]
5
public int PipelineDepth { get; set; }
6
7
8
[GlobalSetup]
9
public void SetUp() {
10
behaviorContext = new BehaviorContext();
11
12
pipelineModificationsBeforeOptimizations = new PipelineModifications();
13
for (int i = 0; i < PipelineDepth; i++)
14
{
15
pipelineModificationsBeforeOptimizations.Additions.Add(RegisterStep.Create(i.ToString(),
16
typeof(BaseLineBehavior), i.ToString(), b => new BaseLineBehavior()));
17
}
18
19
pipelineModificationsAfterOptimizations = new PipelineModifications();
20
for (int i = 0; i < PipelineDepth; i++)
21
{
22
pipelineModificationsAfterOptimizations.Additions.Add(RegisterStep.Create(i.ToString(),
23
typeof(BehaviorOptimization), i.ToString(), b => new BehaviorOptimization()));
24
}
25
26
pipelineBeforeOptimizations = new BaseLinePipeline<IBehaviorContext>(null, new SettingsHolder(),
27
pipelineModificationsBeforeOptimizations);
28
pipelineAfterOptimizations = new PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(),
29
pipelineModificationsAfterOptimizations);
30
}
31
32
33
34
35
36
37
38
39
40
41
}
42
[ShortRunJob]
[MemoryDiagnoser]
public class PipelineExecution {
[Params(10, 20, 40)]
public int PipelineDepth { get; set; }
[GlobalSetup]
public void SetUp() {
behaviorContext = new BehaviorContext();
pipelineModificationsBeforeOptimizations = new PipelineModifications();
for (int i = 0; i < PipelineDepth; i++)
{
pipelineModificationsBeforeOptimizations.Additions.Add(RegisterStep.Create(i.ToString(),
typeof(BaseLineBehavior), i.ToString(), b => new BaseLineBehavior()));
}
pipelineModificationsAfterOptimizations = new PipelineModifications();
for (int i = 0; i < PipelineDepth; i++)
{
pipelineModificationsAfterOptimizations.Additions.Add(RegisterStep.Create(i.ToString(),
typeof(BehaviorOptimization), i.ToString(), b => new BehaviorOptimization()));
}
pipelineBeforeOptimizations = new BaseLinePipeline<IBehaviorContext>(null, new SettingsHolder(),
pipelineModificationsBeforeOptimizations);
pipelineAfterOptimizations = new PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(),
pipelineModificationsAfterOptimizations);
}
[Benchmark(Baseline = true)]
public async Task Before() {
await pipelineBeforeOptimizations.Invoke(behaviorContext);
}
[Benchmark]
public async Task After() {
await pipelineAfterOptimizations.Invoke(behaviorContext);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
 Benchmarking the pipeline
Single Responsibility Principle
No side effects
Prevents dead code elimination
Delegates heavy lifting to the
framework
Is explicit
No implicit casting
No var
Avoid running any other resource-
heavy processes while benchmarking
PRACTICES
 Benchmarking the pipeline
Benchmarking is really hard
BenchmarkDotNet will protect you from the common pitfalls
because it does all the dirty work for you
[ShortRunJob]
[MemoryDiagnoser]
public class Step1_PipelineWarmup {
// rest almost the same
[Benchmark(Baseline = true)]
public BaseLinePipeline<IBehaviorContext> Before() {
var pipelineBeforeOptimizations = new
BaseLinePipeline<IBehaviorContext>(null, new SettingsHolder(),
pipelineModificationsBeforeOptimizations);
return pipelineBeforeOptimizations;
}
[Benchmark]
public PipelineOptimization<IBehaviorContext> After() {
var pipelineAfterOptimizations = new
PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(),
pipelineModificationsAfterOptimizations);
return pipelineAfterOptimizations;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 Benchmarking the pipeline
[ShortRunJob]
[MemoryDiagnoser]
public class Step2_PipelineException {
[GlobalSetup]
public void SetUp() {
...
var stepdId = PipelineDepth + 1;
pipelineModificationsBeforeOptimizations.Additions.Add(RegisterStep.Create(stepdId.ToString(), typeof(Throwing), "1", b
=> new Throwing()));
...
pipelineModificationsAfterOptimizations.Additions.Add(RegisterStep.Create(stepdId.ToString(), typeof(Throwing), "1", b
=> new Throwing()));
pipelineBeforeOptimizations = new Step1.PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(),
pipelineModificationsBeforeOptimizations);
pipelineAfterOptimizations = new PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(),
pipelineModificationsAfterOptimizations);
}
[Benchmark(Baseline = true)]
public async Task Before() {
try
{
await pipelineBeforeOptimizations.Invoke(behaviorContext).ConfigureAwait(false);
}
catch (InvalidOperationException)
{
}
}
[Benchmark]
public async Task After() {
try
{
await pipelineAfterOptimizations.Invoke(behaviorContext).ConfigureAwait(false);
}
catch (InvalidOperationException)
{
}
}
class Throwing : Behavior<IBehaviorContext> {
public override Task Invoke(IBehaviorContext context, Func<Task> next)
{
throw new InvalidOperationException();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
 Benchmarking the pipeline
[ShortRunJob]
[MemoryDiagnoser]
public class Step2_PipelineException {
[GlobalSetup]
public void SetUp() {
...
var stepdId = PipelineDepth + 1;
pipelineModificationsBeforeOptimizations.Additions.Add(RegisterStep.Create(stepd
Id.ToString(), typeof(Throwing), "1", b => new Throwing()));
...
pipelineModificationsAfterOptimizations.Additions.Add(RegisterStep.Create(stepdI
d.ToString(), typeof(Throwing), "1", b => new Throwing()));
pipelineBeforeOptimizations = new
Step1.PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(),
pipelineModificationsBeforeOptimizations);
pipelineAfterOptimizations = new PipelineOptimization<IBehaviorContext>
(null, new SettingsHolder(),
pipelineModificationsAfterOptimizations);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
pipelineModificationsBeforeOptimizations.Additions.Add(RegisterStep.Create(stepd
Id.ToString(), typeof(Throwing), "1", b => new Throwing()));
pipelineModificationsAfterOptimizations.Additions.Add(RegisterStep.Create(stepdI
d.ToString(), typeof(Throwing), "1", b => new Throwing()));
[ShortRunJob]
1
[MemoryDiagnoser]
2
public class Step2_PipelineException {
3
[GlobalSetup]
4
public void SetUp() {
5
...
6
var stepdId = PipelineDepth + 1;
7
8
9
...
10
11
12
pipelineBeforeOptimizations = new
Step1.PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(),
13
pipelineModificationsBeforeOptimizations);
14
pipelineAfterOptimizations = new PipelineOptimization<IBehaviorContext>
(null, new SettingsHolder(),
15
pipelineModificationsAfterOptimizations);
16
}
17
}
18
 Benchmarking the pipeline
[ShortRunJob]
[MemoryDiagnoser]
public class Step2_PipelineException {
[GlobalSetup]
public void SetUp() {
...
}
[Benchmark(Baseline = true)]
public async Task Before() {
try
{
await pipelineBeforeOptimizations.Invoke(behaviorContext);
}
catch (InvalidOperationException)
{
}
}
[Benchmark]
public async Task After() {
try
{
await pipelineAfterOptimizations.Invoke(behaviorContext);
}
catch (InvalidOperationException)
{
}
}
...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 Benchmarking the pipeline
 Benchmarking the pipeline
PROFILING THE PIPELINE (AGAIN)
 Profiling the pipeline (again)
PUBLISH
MEMORY CHARACTERISTICS
BEFORE AFTER
RECEIVE
MEMORY CHARACTERISTICS
 Profiling the pipeline (again)
AFTER
BEFORE
MEMORY CHARACTERISTICS
 Profiling the pipeline (again)
RECEIVE AFTER
BEFORE
MEMORY CHARACTERISTICS
 Profiling the pipeline (again)
AFTER
BEFORE
CPU CHARACTERISTICS
PUBLISH
 Profiling the pipeline (again)
CPU CHARACTERISTICS
RECEIVE
 Profiling the pipeline (again)
NServiceBus Pipeline
NServiceBus Transport
MSMQ
GETTING LOWER ON THE STACK
NServiceBus Pipeline
NServiceBus Transport
Azure.Messaging.ServiceBus
Microsoft.Azure.Amqp
GETTING LOWER ON THE STACK
THE HARNESS
await using var serviceBusClient = new ServiceBusClient(connectionString);
await using var sender = serviceBusClient.CreateSender(destination);
var messages = new List<ServiceBusMessage>(1000);
for (int i = 0; i < 1000; i++) {
messages.Add(new ServiceBusMessage(UTF8.GetBytes($"Deep Dive {i} Deep Dive {i} Deep Dive {i} Deep Dive {i} Deep Dive {i} Deep Dive {i}")));
if (i % 100 == 0) {
await sender.SendMessagesAsync(messages);
messages.Clear();
}
}
await sender.SendMessagesAsync(messages);
WriteLine("Messages sent");
Console.WriteLine("Take snapshot");
Console.ReadLine();
var countDownEvent = new CountdownEvent(1000);
var processorOptions = new ServiceBusProcessorOptions {
AutoCompleteMessages = true,
MaxConcurrentCalls = 100,
MaxAutoLockRenewalDuration = TimeSpan.FromMinutes(10),
ReceiveMode = ServiceBusReceiveMode.PeekLock,
};
await using var receiver = serviceBusClient.CreateProcessor(destination, processorOptions);
receiver.ProcessMessageAsync += async messageEventArgs => {
var message = messageEventArgs.Message;
await Out.WriteLineAsync(
$"Received message with '{message.MessageId}' and content '{UTF8.GetString(message.Body)}' / binary {message.Body}");
countDownEvent.Signal();
};
// rest omitted
await receiver.StartProcessingAsync();
countDownEvent.Wait();
Console.WriteLine("Take snapshot");
Console.ReadLine();
await receiver.StopProcessingAsync();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
Getting lower on the stack
THE HARNESS
await using var serviceBusClient = new ServiceBusClient(connectionString);
await using var sender = serviceBusClient.CreateSender(destination);
var messages = new List<ServiceBusMessage>(1000);
for (int i = 0; i < 1000; i++) {
messages.Add(new ServiceBusMessage(UTF8.GetBytes($"Deep Dive {i} Deep Dive {i} Deep Dive {i} Deep Dive {i} Deep
Dive {i} Deep Dive {i}")));
if (i % 100 == 0) {
await sender.SendMessagesAsync(messages);
messages.Clear();
}
}
await sender.SendMessagesAsync(messages);
WriteLine("Messages sent");
Console.WriteLine("Take snapshot");
Console.ReadLine();
Getting lower on the stack
THE HARNESS
receiver.ProcessMessageAsync += async messageEventArgs => {
var message = messageEventArgs.Message;
await Out.WriteLineAsync(
$"Received message with '{message.MessageId}' and content '{UTF8.GetString(message.Body)}' / binary
{message.Body}");
countDownEvent.Signal();
};
var countDownEvent = new CountdownEvent(1000);
1
2
var processorOptions = new ServiceBusProcessorOptions
3
{
4
AutoCompleteMessages = true,
5
MaxConcurrentCalls = 100,
6
MaxAutoLockRenewalDuration = TimeSpan.FromMinutes(10),
7
ReceiveMode = ServiceBusReceiveMode.PeekLock,
8
};
9
10
await using var receiver = serviceBusClient.CreateProcessor(destination, processorOptions);
11
12
13
14
15
16
17
// rest omitted
18
await receiver.StartProcessingAsync();
19
20
countDownEvent.Wait();
21
22
Console.WriteLine("Take snapshot");
23
Console.ReadLine();
24
25
await receiver.StopProcessingAsync();
26
Getting lower on the stack
MEMORY CHARACTERISTICS
Getting lower on the stack
MEMORY CHARACTERISTICS
Getting lower on the stack
PREVENTING REGRESSIONS
C:ProjectsperformancesrctoolsResultsComparer>
dotnet run --base "C:resultsbefore"
--diff "C:resultsafter" --threshold 2%
1
2
Guidance
Tool
Preventing Regressions
ResultComparer C:Projectsperformancesrcbenchmarksmicro>
dotnet run -c Release -f net8.0 
--artifacts "C:resultsbefore"
1
2
C:Projectsperformancesrcbenchmarksmicro>
dotnet run -c Release -f net8.0 
--artifacts "C:resultsafter"
1
2
"Two subsequent builds on the same revision can have ranges of
1.5..2 seconds and 12..36 seconds. CPU-bound benchmarks are
much more stable than Memory/Disk-bound benchmarks, but
the “average” performance levels still can be up to three times
different across builds."
Andrey Akinshin - Performance stability of GitHub Actions
BEYOND SIMPLE BENCHMARKS
A PRACTICAL GUIDE TO OPTIMIZING CODE
 | ✉ | 
danielmarbach daniel.marbach@particular.net Daniel Marbach
github.com/danielmarbach/BeyondSimpleBenchmarks
Use the performance loop to improve your code where it
matters
Combine it with profiling to observe how the small changes
add up
Optimize until you hit a diminishing point of return
You'll learn a ton about potential improvements for a new
design

Weitere ähnliche Inhalte

Ähnlich wie Code Optimization Guide

[NDC 2019] Enterprise-Grade Serverless
[NDC 2019] Enterprise-Grade Serverless[NDC 2019] Enterprise-Grade Serverless
[NDC 2019] Enterprise-Grade ServerlessKatyShimizu
 
Better Open Source Enterprise C++ Web Services
Better Open Source Enterprise C++ Web ServicesBetter Open Source Enterprise C++ Web Services
Better Open Source Enterprise C++ Web ServicesWSO2
 
maxbox starter72 multilanguage coding
maxbox starter72 multilanguage codingmaxbox starter72 multilanguage coding
maxbox starter72 multilanguage codingMax Kleiner
 
Angular 1 + es6
Angular 1 + es6Angular 1 + es6
Angular 1 + es6장현 한
 
Advanced iOS Debbuging (Reloaded)
Advanced iOS Debbuging (Reloaded)Advanced iOS Debbuging (Reloaded)
Advanced iOS Debbuging (Reloaded)Massimo Oliviero
 
Samsung WebCL Prototype API
Samsung WebCL Prototype APISamsung WebCL Prototype API
Samsung WebCL Prototype APIRyo Jin
 
Nagios Conference 2012 - Dave Josephsen - Stop Being Lazy
Nagios Conference 2012 - Dave Josephsen - Stop Being LazyNagios Conference 2012 - Dave Josephsen - Stop Being Lazy
Nagios Conference 2012 - Dave Josephsen - Stop Being LazyNagios
 
2016 W3C Conference #4 : ANGULAR + ES6
2016 W3C Conference #4 : ANGULAR + ES62016 W3C Conference #4 : ANGULAR + ES6
2016 W3C Conference #4 : ANGULAR + ES6양재동 코드랩
 
Taming startup dynamics - Magnus Jungsbluth & Domagoj Cosic
Taming startup dynamics - Magnus Jungsbluth & Domagoj CosicTaming startup dynamics - Magnus Jungsbluth & Domagoj Cosic
Taming startup dynamics - Magnus Jungsbluth & Domagoj Cosicmfrancis
 
Running Vue Storefront in production (PWA Magento webshop)
Running Vue Storefront in production (PWA Magento webshop)Running Vue Storefront in production (PWA Magento webshop)
Running Vue Storefront in production (PWA Magento webshop)Vendic Magento, PWA & Marketing
 
How to make a high-quality Node.js app, Nikita Galkin
How to make a high-quality Node.js app, Nikita GalkinHow to make a high-quality Node.js app, Nikita Galkin
How to make a high-quality Node.js app, Nikita GalkinSigma Software
 
"Load Testing Distributed Systems with NBomber 4.0", Anton Moldovan
"Load Testing Distributed Systems with NBomber 4.0",  Anton Moldovan"Load Testing Distributed Systems with NBomber 4.0",  Anton Moldovan
"Load Testing Distributed Systems with NBomber 4.0", Anton MoldovanFwdays
 
PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerC...
PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerC...PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerC...
PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerC...Andrey Karpov
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksMongoDB
 
Flink Forward Berlin 2017: Maciek Próchniak - TouK Nussknacker - creating Fli...
Flink Forward Berlin 2017: Maciek Próchniak - TouK Nussknacker - creating Fli...Flink Forward Berlin 2017: Maciek Próchniak - TouK Nussknacker - creating Fli...
Flink Forward Berlin 2017: Maciek Próchniak - TouK Nussknacker - creating Fli...Flink Forward
 
TestUpload
TestUploadTestUpload
TestUploadZarksaDS
 
Building a Serverless company with Node.js, React and the Serverless Framewor...
Building a Serverless company with Node.js, React and the Serverless Framewor...Building a Serverless company with Node.js, React and the Serverless Framewor...
Building a Serverless company with Node.js, React and the Serverless Framewor...Luciano Mammino
 

Ähnlich wie Code Optimization Guide (20)

[NDC 2019] Enterprise-Grade Serverless
[NDC 2019] Enterprise-Grade Serverless[NDC 2019] Enterprise-Grade Serverless
[NDC 2019] Enterprise-Grade Serverless
 
C#on linux
C#on linuxC#on linux
C#on linux
 
Better Open Source Enterprise C++ Web Services
Better Open Source Enterprise C++ Web ServicesBetter Open Source Enterprise C++ Web Services
Better Open Source Enterprise C++ Web Services
 
maxbox starter72 multilanguage coding
maxbox starter72 multilanguage codingmaxbox starter72 multilanguage coding
maxbox starter72 multilanguage coding
 
Angular 1 + es6
Angular 1 + es6Angular 1 + es6
Angular 1 + es6
 
Advanced iOS Debbuging (Reloaded)
Advanced iOS Debbuging (Reloaded)Advanced iOS Debbuging (Reloaded)
Advanced iOS Debbuging (Reloaded)
 
Samsung WebCL Prototype API
Samsung WebCL Prototype APISamsung WebCL Prototype API
Samsung WebCL Prototype API
 
Nagios Conference 2012 - Dave Josephsen - Stop Being Lazy
Nagios Conference 2012 - Dave Josephsen - Stop Being LazyNagios Conference 2012 - Dave Josephsen - Stop Being Lazy
Nagios Conference 2012 - Dave Josephsen - Stop Being Lazy
 
[W3C HTML5 2016] Angular + ES6
[W3C HTML5 2016] Angular + ES6[W3C HTML5 2016] Angular + ES6
[W3C HTML5 2016] Angular + ES6
 
2016 W3C Conference #4 : ANGULAR + ES6
2016 W3C Conference #4 : ANGULAR + ES62016 W3C Conference #4 : ANGULAR + ES6
2016 W3C Conference #4 : ANGULAR + ES6
 
Taming startup dynamics - Magnus Jungsbluth & Domagoj Cosic
Taming startup dynamics - Magnus Jungsbluth & Domagoj CosicTaming startup dynamics - Magnus Jungsbluth & Domagoj Cosic
Taming startup dynamics - Magnus Jungsbluth & Domagoj Cosic
 
Running Vue Storefront in production (PWA Magento webshop)
Running Vue Storefront in production (PWA Magento webshop)Running Vue Storefront in production (PWA Magento webshop)
Running Vue Storefront in production (PWA Magento webshop)
 
How to make a high-quality Node.js app, Nikita Galkin
How to make a high-quality Node.js app, Nikita GalkinHow to make a high-quality Node.js app, Nikita Galkin
How to make a high-quality Node.js app, Nikita Galkin
 
"Load Testing Distributed Systems with NBomber 4.0", Anton Moldovan
"Load Testing Distributed Systems with NBomber 4.0",  Anton Moldovan"Load Testing Distributed Systems with NBomber 4.0",  Anton Moldovan
"Load Testing Distributed Systems with NBomber 4.0", Anton Moldovan
 
PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerC...
PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerC...PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerC...
PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerC...
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New Tricks
 
Flink Forward Berlin 2017: Maciek Próchniak - TouK Nussknacker - creating Fli...
Flink Forward Berlin 2017: Maciek Próchniak - TouK Nussknacker - creating Fli...Flink Forward Berlin 2017: Maciek Próchniak - TouK Nussknacker - creating Fli...
Flink Forward Berlin 2017: Maciek Próchniak - TouK Nussknacker - creating Fli...
 
Ceilometer + Heat = Alarming
Ceilometer + Heat = Alarming Ceilometer + Heat = Alarming
Ceilometer + Heat = Alarming
 
TestUpload
TestUploadTestUpload
TestUpload
 
Building a Serverless company with Node.js, React and the Serverless Framewor...
Building a Serverless company with Node.js, React and the Serverless Framewor...Building a Serverless company with Node.js, React and the Serverless Framewor...
Building a Serverless company with Node.js, React and the Serverless Framewor...
 

Mehr von Particular Software

Scaling for Success: Lessons from handling peak loads on Azure with NServiceBus
Scaling for Success: Lessons from handling peak loads on Azure with NServiceBusScaling for Success: Lessons from handling peak loads on Azure with NServiceBus
Scaling for Success: Lessons from handling peak loads on Azure with NServiceBusParticular Software
 
An exception occurred - Please try again
An exception occurred - Please try againAn exception occurred - Please try again
An exception occurred - Please try againParticular Software
 
Tales from the trenches creating complex distributed systems
Tales from the trenches  creating complex distributed systemsTales from the trenches  creating complex distributed systems
Tales from the trenches creating complex distributed systemsParticular Software
 
Implementing outbox model-checking first
Implementing outbox   model-checking firstImplementing outbox   model-checking first
Implementing outbox model-checking firstParticular Software
 
Reports from the field azure functions in practice
Reports from the field   azure functions in practiceReports from the field   azure functions in practice
Reports from the field azure functions in practiceParticular Software
 
Finding your service boundaries - a practical guide
Finding your service boundaries - a practical guideFinding your service boundaries - a practical guide
Finding your service boundaries - a practical guideParticular Software
 
Decomposing .NET Monoliths with NServiceBus and Docker
Decomposing .NET Monoliths with NServiceBus and DockerDecomposing .NET Monoliths with NServiceBus and Docker
Decomposing .NET Monoliths with NServiceBus and DockerParticular Software
 
DIY Async Message Pump: Lessons from the trenches
DIY Async Message Pump: Lessons from the trenchesDIY Async Message Pump: Lessons from the trenches
DIY Async Message Pump: Lessons from the trenchesParticular Software
 
Share the insight of ServiceInsight
Share the insight of ServiceInsightShare the insight of ServiceInsight
Share the insight of ServiceInsightParticular Software
 
What to consider when monitoring microservices
What to consider when monitoring microservicesWhat to consider when monitoring microservices
What to consider when monitoring microservicesParticular Software
 
Making communications across boundaries simple with NServiceBus
Making communications across boundaries simple with NServiceBusMaking communications across boundaries simple with NServiceBus
Making communications across boundaries simple with NServiceBusParticular Software
 
Making communication across boundaries simple with Azure Service Bus
Making communication across boundaries simple with Azure Service BusMaking communication across boundaries simple with Azure Service Bus
Making communication across boundaries simple with Azure Service BusParticular Software
 
How to avoid microservice pitfalls
How to avoid microservice pitfallsHow to avoid microservice pitfalls
How to avoid microservice pitfallsParticular Software
 
Connect front end to back end using SignalR and Messaging
Connect front end to back end using SignalR and MessagingConnect front end to back end using SignalR and Messaging
Connect front end to back end using SignalR and MessagingParticular Software
 
Async/Await: NServiceBus v6 API Update
Async/Await: NServiceBus v6 API UpdateAsync/Await: NServiceBus v6 API Update
Async/Await: NServiceBus v6 API UpdateParticular Software
 
Async/Await: TPL & Message Pumps
Async/Await: TPL & Message Pumps Async/Await: TPL & Message Pumps
Async/Await: TPL & Message Pumps Particular Software
 
Making workflow implementation easy with CQRS
Making workflow implementation easy with CQRSMaking workflow implementation easy with CQRS
Making workflow implementation easy with CQRSParticular Software
 

Mehr von Particular Software (20)

Scaling for Success: Lessons from handling peak loads on Azure with NServiceBus
Scaling for Success: Lessons from handling peak loads on Azure with NServiceBusScaling for Success: Lessons from handling peak loads on Azure with NServiceBus
Scaling for Success: Lessons from handling peak loads on Azure with NServiceBus
 
An exception occurred - Please try again
An exception occurred - Please try againAn exception occurred - Please try again
An exception occurred - Please try again
 
Tales from the trenches creating complex distributed systems
Tales from the trenches  creating complex distributed systemsTales from the trenches  creating complex distributed systems
Tales from the trenches creating complex distributed systems
 
Got the time?
Got the time?Got the time?
Got the time?
 
Implementing outbox model-checking first
Implementing outbox   model-checking firstImplementing outbox   model-checking first
Implementing outbox model-checking first
 
Reports from the field azure functions in practice
Reports from the field   azure functions in practiceReports from the field   azure functions in practice
Reports from the field azure functions in practice
 
Finding your service boundaries - a practical guide
Finding your service boundaries - a practical guideFinding your service boundaries - a practical guide
Finding your service boundaries - a practical guide
 
Decomposing .NET Monoliths with NServiceBus and Docker
Decomposing .NET Monoliths with NServiceBus and DockerDecomposing .NET Monoliths with NServiceBus and Docker
Decomposing .NET Monoliths with NServiceBus and Docker
 
DIY Async Message Pump: Lessons from the trenches
DIY Async Message Pump: Lessons from the trenchesDIY Async Message Pump: Lessons from the trenches
DIY Async Message Pump: Lessons from the trenches
 
Share the insight of ServiceInsight
Share the insight of ServiceInsightShare the insight of ServiceInsight
Share the insight of ServiceInsight
 
What to consider when monitoring microservices
What to consider when monitoring microservicesWhat to consider when monitoring microservices
What to consider when monitoring microservices
 
Making communications across boundaries simple with NServiceBus
Making communications across boundaries simple with NServiceBusMaking communications across boundaries simple with NServiceBus
Making communications across boundaries simple with NServiceBus
 
Making communication across boundaries simple with Azure Service Bus
Making communication across boundaries simple with Azure Service BusMaking communication across boundaries simple with Azure Service Bus
Making communication across boundaries simple with Azure Service Bus
 
How to avoid microservice pitfalls
How to avoid microservice pitfallsHow to avoid microservice pitfalls
How to avoid microservice pitfalls
 
Connect front end to back end using SignalR and Messaging
Connect front end to back end using SignalR and MessagingConnect front end to back end using SignalR and Messaging
Connect front end to back end using SignalR and Messaging
 
Async/Await: NServiceBus v6 API Update
Async/Await: NServiceBus v6 API UpdateAsync/Await: NServiceBus v6 API Update
Async/Await: NServiceBus v6 API Update
 
Async/Await: TPL & Message Pumps
Async/Await: TPL & Message Pumps Async/Await: TPL & Message Pumps
Async/Await: TPL & Message Pumps
 
Async/Await Best Practices
Async/Await Best PracticesAsync/Await Best Practices
Async/Await Best Practices
 
Making workflow implementation easy with CQRS
Making workflow implementation easy with CQRSMaking workflow implementation easy with CQRS
Making workflow implementation easy with CQRS
 
Cqrs but different
Cqrs but differentCqrs but different
Cqrs but different
 

Kürzlich hochgeladen

React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaHanief Utama
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureDinusha Kumarasiri
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanyChristoph Pohl
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsAhmed Mohamed
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesŁukasz Chruściel
 
MYjobs Presentation Django-based project
MYjobs Presentation Django-based projectMYjobs Presentation Django-based project
MYjobs Presentation Django-based projectAnoyGreter
 
What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....kzayra69
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesPhilip Schwarz
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Hr365.us smith
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024StefanoLambiase
 
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)jennyeacort
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)OPEN KNOWLEDGE GmbH
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWave PLM
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作qr0udbr0
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtimeandrehoraa
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfAlina Yurenko
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEEVICTOR MAESTRE RAMIREZ
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...OnePlan Solutions
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样umasea
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...soniya singh
 

Kürzlich hochgeladen (20)

React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief Utama
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with Azure
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML Diagrams
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
 
MYjobs Presentation Django-based project
MYjobs Presentation Django-based projectMYjobs Presentation Django-based project
MYjobs Presentation Django-based project
 
What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a series
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
 
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need It
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtime
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEE
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
 

Code Optimization Guide

  • 1. BEYOND SIMPLE BENCHMARKS A PRACTICAL GUIDE TO OPTIMIZING CODE  | ✉ |  danielmarbach daniel.marbach@particular.net Daniel Marbach
  • 3.
  • 4. Microsoft Teams’ Infrastructure and Azure Communication Services’ Journey to .NET 6 "We were able to see Azure Compute cost reduction of up to 50% per month, on average we observed 24% monthly cost reduction after migrating to .NET 6. The reduction in cores reduced Azure spend by 24%."
  • 7. BE CURIOUS.... UNDERSTAND THE CONTEXT How is this code going to be executed at scale, and what would the memory characteristics be (gut feeling) Are there simple low-hanging fruits I can apply to accelerate this code? Are there things I can move away from the hot path by simply restructuring a bit my code? What part is under my control and what isn't really? What optimizations can I apply, and when should I stop?
  • 8. THE PERFORMANCE LOOP Profile at least CPU and memory using a profiling harness Improve parts of the hot path Benchmark and compare Profile improvements again with the harness and make adjustments where necessary Ship and focus your attention to other parts
  • 11. ASP.NET CORE MIDDLEWARE public class RequestCultureMiddleware { _next = next; public async Task InvokeAsync(HttpContext context) { await _next(context); 1 private readonly RequestDelegate _next; 2 3 public RequestCultureMiddleware(RequestDelegate next) { 4 5 } 6 7 8 // Do work that does something before 9 10 // Do work that does something after 11 } 12 } 13
  • 12. public class Behavior : Behavior<IIncomingLogicalMessageContext> { public override Task Invoke(IIncomingLogicalMessageContext context, Func<Task> next) { await next(); 1 2 3 // Do work that does something before 4 5 // Do work that does something after 6 } 7 } 8 BEHAVIORS
  • 14. THE HARNESS Compiled and executed in Release mode Runs a few seconds and keeps overhead minimal Disabled Tiered JIT <TieredCompilation>false</TieredCompilation> Emits full symbols <DebugType>pdbonly</DebugType <DebugSymbols>true</DebugSymbols> var endpointConfiguration = new EndpointConfiguration("PublishSample"); endpointConfiguration.UseSerialization<JsonSerializer>(); var transport = endpointConfiguration.UseTransport<MsmqTransport>(); transport.Routing().RegisterPublisher(typeof(MyEvent), "PublishSample"); endpointConfiguration.UsePersistence<InMemoryPersistence>(); endpointConfiguration.EnableInstallers(); endpointConfiguration.SendFailedMessagesTo("error"); var endpointInstance = await Endpoint.Start(endpointConfiguration); Console.WriteLine("Attach the profiler and hit <enter>."); Console.ReadLine(); 1 2 3 4 5 6 7 8 9 10 11 12 13 var tasks = new List<Task>(1000); 14 for (int i = 0; i < 1000; i++) 15 { 16 tasks.Add(endpointInstance.Publish(new MyEvent())); 17 } 18 await Task.WhenAll(tasks); 19 20 Console.WriteLine("Publish 1000 done. Get a snapshot"); 21 Console.ReadLine(); 22 var transport = endpointConfiguration.UseTransport<MsmqTransport>(); var endpointConfiguration = new EndpointConfiguration("PublishSample"); 1 endpointConfiguration.UseSerialization<JsonSerializer>(); 2 3 transport.Routing().RegisterPublisher(typeof(MyEvent), "PublishSample"); 4 endpointConfiguration.UsePersistence<InMemoryPersistence>(); 5 endpointConfiguration.EnableInstallers(); 6 endpointConfiguration.SendFailedMessagesTo("error"); 7 8 var endpointInstance = await Endpoint.Start(endpointConfiguration); 9 10 Console.WriteLine("Attach the profiler and hit <enter>."); 11 Console.ReadLine(); 12 13 var tasks = new List<Task>(1000); 14 for (int i = 0; i < 1000; i++) 15 { 16 tasks.Add(endpointInstance.Publish(new MyEvent())); 17 } 18 await Task.WhenAll(tasks); 19 20 Console.WriteLine("Publish 1000 done. Get a snapshot"); 21 Console.ReadLine(); 22 endpointConfiguration.UseSerialization<JsonSerializer>(); var endpointConfiguration = new EndpointConfiguration("PublishSample"); 1 2 var transport = endpointConfiguration.UseTransport<MsmqTransport>(); 3 transport.Routing().RegisterPublisher(typeof(MyEvent), "PublishSample"); 4 endpointConfiguration.UsePersistence<InMemoryPersistence>(); 5 endpointConfiguration.EnableInstallers(); 6 endpointConfiguration.SendFailedMessagesTo("error"); 7 8 var endpointInstance = await Endpoint.Start(endpointConfiguration); 9 10 Console.WriteLine("Attach the profiler and hit <enter>."); 11 Console.ReadLine(); 12 13 var tasks = new List<Task>(1000); 14 for (int i = 0; i < 1000; i++) 15 { 16 tasks.Add(endpointInstance.Publish(new MyEvent())); 17 } 18 await Task.WhenAll(tasks); 19 20 Console.WriteLine("Publish 1000 done. Get a snapshot"); 21 Console.ReadLine(); 22 endpointConfiguration.UsePersistence<InMemoryPersistence>(); var endpointConfiguration = new EndpointConfiguration("PublishSample"); 1 endpointConfiguration.UseSerialization<JsonSerializer>(); 2 var transport = endpointConfiguration.UseTransport<MsmqTransport>(); 3 transport.Routing().RegisterPublisher(typeof(MyEvent), "PublishSample"); 4 5 endpointConfiguration.EnableInstallers(); 6 endpointConfiguration.SendFailedMessagesTo("error"); 7 8 var endpointInstance = await Endpoint.Start(endpointConfiguration); 9 10 Console.WriteLine("Attach the profiler and hit <enter>."); 11 Console.ReadLine(); 12 13 var tasks = new List<Task>(1000); 14 for (int i = 0; i < 1000; i++) 15 { 16 tasks.Add(endpointInstance.Publish(new MyEvent())); 17 } 18 await Task.WhenAll(tasks); 19 20 Console.WriteLine("Publish 1000 done. Get a snapshot"); 21 Console.ReadLine(); 22 var tasks = new List<Task>(1000); for (int i = 0; i < 1000; i++) { tasks.Add(endpointInstance.Publish(new MyEvent())); } await Task.WhenAll(tasks); var endpointConfiguration = new EndpointConfiguration("PublishSample"); 1 endpointConfiguration.UseSerialization<JsonSerializer>(); 2 var transport = endpointConfiguration.UseTransport<MsmqTransport>(); 3 transport.Routing().RegisterPublisher(typeof(MyEvent), "PublishSample"); 4 endpointConfiguration.UsePersistence<InMemoryPersistence>(); 5 endpointConfiguration.EnableInstallers(); 6 endpointConfiguration.SendFailedMessagesTo("error"); 7 8 var endpointInstance = await Endpoint.Start(endpointConfiguration); 9 10 Console.WriteLine("Attach the profiler and hit <enter>."); 11 Console.ReadLine(); 12 13 14 15 16 17 18 19 20 Console.WriteLine("Publish 1000 done. Get a snapshot"); 21 Console.ReadLine(); 22 var endpointConfiguration = new EndpointConfiguration("PublishSample"); endpointConfiguration.UseSerialization<JsonSerializer>(); var transport = endpointConfiguration.UseTransport<MsmqTransport>(); transport.Routing().RegisterPublisher(typeof(MyEvent), "PublishSample"); endpointConfiguration.UsePersistence<InMemoryPersistence>(); endpointConfiguration.EnableInstallers(); endpointConfiguration.SendFailedMessagesTo("error"); var endpointInstance = await Endpoint.Start(endpointConfiguration); Console.WriteLine("Attach the profiler and hit <enter>."); Console.ReadLine(); var tasks = new List<Task>(1000); for (int i = 0; i < 1000; i++) { tasks.Add(endpointInstance.Publish(new MyEvent())); } await Task.WhenAll(tasks); Console.WriteLine("Publish 1000 done. Get a snapshot"); Console.ReadLine(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class MyEventHandler : IHandleMessages<MyEvent> { public Task Handle(MyEvent message, IMessageHandlerContext context) { Console.WriteLine("Event received"); return Task.CompletedTask; } }  Profiling the pipeline
  • 27.  10X faster execution with compiled expression trees  How we achieved 5X faster pipeline execution by removing closure allocations IMPROVING go.particular.net/webinar-2023-pipeline
  • 29.  Benchmarking the pipeline Copy and paste relevant code Adjust it to the bare essentials to create a controllable environment EXTRACT CODE
  • 30. Trim down to relevant behaviors Replaced dependency injection container with creating relevant classes Replaced IO-operations with completed tasks EXTRACT CODE  Benchmarking the pipeline
  • 31. Get started with small steps Culture change takes time Make changes gradually PERFORMANCE CULTURE
  • 32. [ShortRunJob] [MemoryDiagnoser] public class PipelineExecution { [Params(10, 20, 40)] public int PipelineDepth { get; set; } [GlobalSetup] public void SetUp() { behaviorContext = new BehaviorContext(); pipelineModificationsBeforeOptimizations = new PipelineModifications(); for (int i = 0; i < PipelineDepth; i++) { pipelineModificationsBeforeOptimizations.Additions.Add(RegisterStep.Create(i.ToString(), typeof(BaseLineBehavior), i.ToString(), b => new BaseLineBehavior())); } pipelineModificationsAfterOptimizations = new PipelineModifications(); for (int i = 0; i < PipelineDepth; i++) { pipelineModificationsAfterOptimizations.Additions.Add(RegisterStep.Create(i.ToString(), typeof(BehaviorOptimization), i.ToString(), b => new BehaviorOptimization())); } pipelineBeforeOptimizations = new BaseLinePipeline<IBehaviorContext>(null, new SettingsHolder(), pipelineModificationsBeforeOptimizations); pipelineAfterOptimizations = new PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(), pipelineModificationsAfterOptimizations); } [Benchmark(Baseline = true)] public async Task Before() { await pipelineBeforeOptimizations.Invoke(behaviorContext); } [Benchmark] public async Task After() { await pipelineAfterOptimizations.Invoke(behaviorContext); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42  Benchmarking the pipeline
  • 33. [GlobalSetup] public void SetUp() { behaviorContext = new BehaviorContext(); pipelineModificationsBeforeOptimizations = new PipelineModifications(); for (int i = 0; i < PipelineDepth; i++) { pipelineModificationsBeforeOptimizations.Additions.Add(RegisterStep.Create(i.ToString(), typeof(BaseLineBehavior), i.ToString(), b => new BaseLineBehavior())); } pipelineModificationsAfterOptimizations = new PipelineModifications(); for (int i = 0; i < PipelineDepth; i++) { pipelineModificationsAfterOptimizations.Additions.Add(RegisterStep.Create(i.ToString(), typeof(BehaviorOptimization), i.ToString(), b => new BehaviorOptimization())); } pipelineBeforeOptimizations = new BaseLinePipeline<IBehaviorContext>(null, new SettingsHolder(), pipelineModificationsBeforeOptimizations); pipelineAfterOptimizations = new PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(), pipelineModificationsAfterOptimizations); } [ShortRunJob] 1 [MemoryDiagnoser] 2 public class PipelineExecution { 3 4 [Params(10, 20, 40)] 5 public int PipelineDepth { get; set; } 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 [Benchmark(Baseline = true)] 33 public async Task Before() { 34 await pipelineBeforeOptimizations.Invoke(behaviorContext); 35 } 36 37 [Benchmark] 38 public async Task After() { 39 await pipelineAfterOptimizations.Invoke(behaviorContext); 40 } 41 } 42  Benchmarking the pipeline
  • 34. [Params(10, 20, 40)] public int PipelineDepth { get; set; } for (int i = 0; i < PipelineDepth; i++) for (int i = 0; i < PipelineDepth; i++) [ShortRunJob] 1 [MemoryDiagnoser] 2 public class PipelineExecution { 3 4 5 6 7 8 [GlobalSetup] 9 public void SetUp() { 10 behaviorContext = new BehaviorContext(); 11 12 pipelineModificationsBeforeOptimizations = new PipelineModifications(); 13 14 { 15 pipelineModificationsBeforeOptimizations.Additions.Add(RegisterStep.Create(i.ToString(), 16 typeof(BaseLineBehavior), i.ToString(), b => new BaseLineBehavior())); 17 } 18 19 pipelineModificationsAfterOptimizations = new PipelineModifications(); 20 21 { 22 pipelineModificationsAfterOptimizations.Additions.Add(RegisterStep.Create(i.ToString(), 23 typeof(BehaviorOptimization), i.ToString(), b => new BehaviorOptimization())); 24 } 25 26 pipelineBeforeOptimizations = new BaseLinePipeline<IBehaviorContext>(null, new SettingsHolder(), 27 pipelineModificationsBeforeOptimizations); 28 pipelineAfterOptimizations = new PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(), 29 pipelineModificationsAfterOptimizations); 30 } 31 32 [Benchmark(Baseline = true)] 33 public async Task Before() { 34 await pipelineBeforeOptimizations.Invoke(behaviorContext); 35 } 36 37 [Benchmark] 38 public async Task After() { 39 await pipelineAfterOptimizations.Invoke(behaviorContext); 40 } 41 } 42 [ShortRunJob] [MemoryDiagnoser] 1 2 public class PipelineExecution { 3 4 [Params(10, 20, 40)] 5 public int PipelineDepth { get; set; } 6 7 8 [GlobalSetup] 9 public void SetUp() { 10 behaviorContext = new BehaviorContext(); 11 12 pipelineModificationsBeforeOptimizations = new PipelineModifications(); 13 for (int i = 0; i < PipelineDepth; i++) 14 { 15 pipelineModificationsBeforeOptimizations.Additions.Add(RegisterStep.Create(i.ToString(), 16 typeof(BaseLineBehavior), i.ToString(), b => new BaseLineBehavior())); 17 } 18 19 pipelineModificationsAfterOptimizations = new PipelineModifications(); 20 for (int i = 0; i < PipelineDepth; i++) 21 { 22 pipelineModificationsAfterOptimizations.Additions.Add(RegisterStep.Create(i.ToString(), 23 typeof(BehaviorOptimization), i.ToString(), b => new BehaviorOptimization())); 24 } 25 26 pipelineBeforeOptimizations = new BaseLinePipeline<IBehaviorContext>(null, new SettingsHolder(), 27 pipelineModificationsBeforeOptimizations); 28 pipelineAfterOptimizations = new PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(), 29 pipelineModificationsAfterOptimizations); 30 } 31 32 [Benchmark(Baseline = true)] 33 public async Task Before() { 34 await pipelineBeforeOptimizations.Invoke(behaviorContext); 35 } 36 37 [Benchmark] 38 public async Task After() { 39 await pipelineAfterOptimizations.Invoke(behaviorContext); 40 } 41 } 42 [Benchmark(Baseline = true)] public async Task Before() { await pipelineBeforeOptimizations.Invoke(behaviorContext); } [Benchmark] public async Task After() { await pipelineAfterOptimizations.Invoke(behaviorContext); } [ShortRunJob] 1 [MemoryDiagnoser] 2 public class PipelineExecution { 3 4 [Params(10, 20, 40)] 5 public int PipelineDepth { get; set; } 6 7 8 [GlobalSetup] 9 public void SetUp() { 10 behaviorContext = new BehaviorContext(); 11 12 pipelineModificationsBeforeOptimizations = new PipelineModifications(); 13 for (int i = 0; i < PipelineDepth; i++) 14 { 15 pipelineModificationsBeforeOptimizations.Additions.Add(RegisterStep.Create(i.ToString(), 16 typeof(BaseLineBehavior), i.ToString(), b => new BaseLineBehavior())); 17 } 18 19 pipelineModificationsAfterOptimizations = new PipelineModifications(); 20 for (int i = 0; i < PipelineDepth; i++) 21 { 22 pipelineModificationsAfterOptimizations.Additions.Add(RegisterStep.Create(i.ToString(), 23 typeof(BehaviorOptimization), i.ToString(), b => new BehaviorOptimization())); 24 } 25 26 pipelineBeforeOptimizations = new BaseLinePipeline<IBehaviorContext>(null, new SettingsHolder(), 27 pipelineModificationsBeforeOptimizations); 28 pipelineAfterOptimizations = new PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(), 29 pipelineModificationsAfterOptimizations); 30 } 31 32 33 34 35 36 37 38 39 40 41 } 42 [ShortRunJob] [MemoryDiagnoser] public class PipelineExecution { [Params(10, 20, 40)] public int PipelineDepth { get; set; } [GlobalSetup] public void SetUp() { behaviorContext = new BehaviorContext(); pipelineModificationsBeforeOptimizations = new PipelineModifications(); for (int i = 0; i < PipelineDepth; i++) { pipelineModificationsBeforeOptimizations.Additions.Add(RegisterStep.Create(i.ToString(), typeof(BaseLineBehavior), i.ToString(), b => new BaseLineBehavior())); } pipelineModificationsAfterOptimizations = new PipelineModifications(); for (int i = 0; i < PipelineDepth; i++) { pipelineModificationsAfterOptimizations.Additions.Add(RegisterStep.Create(i.ToString(), typeof(BehaviorOptimization), i.ToString(), b => new BehaviorOptimization())); } pipelineBeforeOptimizations = new BaseLinePipeline<IBehaviorContext>(null, new SettingsHolder(), pipelineModificationsBeforeOptimizations); pipelineAfterOptimizations = new PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(), pipelineModificationsAfterOptimizations); } [Benchmark(Baseline = true)] public async Task Before() { await pipelineBeforeOptimizations.Invoke(behaviorContext); } [Benchmark] public async Task After() { await pipelineAfterOptimizations.Invoke(behaviorContext); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42  Benchmarking the pipeline
  • 35. Single Responsibility Principle No side effects Prevents dead code elimination Delegates heavy lifting to the framework Is explicit No implicit casting No var Avoid running any other resource- heavy processes while benchmarking PRACTICES  Benchmarking the pipeline
  • 36. Benchmarking is really hard BenchmarkDotNet will protect you from the common pitfalls because it does all the dirty work for you
  • 37. [ShortRunJob] [MemoryDiagnoser] public class Step1_PipelineWarmup { // rest almost the same [Benchmark(Baseline = true)] public BaseLinePipeline<IBehaviorContext> Before() { var pipelineBeforeOptimizations = new BaseLinePipeline<IBehaviorContext>(null, new SettingsHolder(), pipelineModificationsBeforeOptimizations); return pipelineBeforeOptimizations; } [Benchmark] public PipelineOptimization<IBehaviorContext> After() { var pipelineAfterOptimizations = new PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(), pipelineModificationsAfterOptimizations); return pipelineAfterOptimizations; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19  Benchmarking the pipeline
  • 38. [ShortRunJob] [MemoryDiagnoser] public class Step2_PipelineException { [GlobalSetup] public void SetUp() { ... var stepdId = PipelineDepth + 1; pipelineModificationsBeforeOptimizations.Additions.Add(RegisterStep.Create(stepdId.ToString(), typeof(Throwing), "1", b => new Throwing())); ... pipelineModificationsAfterOptimizations.Additions.Add(RegisterStep.Create(stepdId.ToString(), typeof(Throwing), "1", b => new Throwing())); pipelineBeforeOptimizations = new Step1.PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(), pipelineModificationsBeforeOptimizations); pipelineAfterOptimizations = new PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(), pipelineModificationsAfterOptimizations); } [Benchmark(Baseline = true)] public async Task Before() { try { await pipelineBeforeOptimizations.Invoke(behaviorContext).ConfigureAwait(false); } catch (InvalidOperationException) { } } [Benchmark] public async Task After() { try { await pipelineAfterOptimizations.Invoke(behaviorContext).ConfigureAwait(false); } catch (InvalidOperationException) { } } class Throwing : Behavior<IBehaviorContext> { public override Task Invoke(IBehaviorContext context, Func<Task> next) { throw new InvalidOperationException(); } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47  Benchmarking the pipeline
  • 39. [ShortRunJob] [MemoryDiagnoser] public class Step2_PipelineException { [GlobalSetup] public void SetUp() { ... var stepdId = PipelineDepth + 1; pipelineModificationsBeforeOptimizations.Additions.Add(RegisterStep.Create(stepd Id.ToString(), typeof(Throwing), "1", b => new Throwing())); ... pipelineModificationsAfterOptimizations.Additions.Add(RegisterStep.Create(stepdI d.ToString(), typeof(Throwing), "1", b => new Throwing())); pipelineBeforeOptimizations = new Step1.PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(), pipelineModificationsBeforeOptimizations); pipelineAfterOptimizations = new PipelineOptimization<IBehaviorContext> (null, new SettingsHolder(), pipelineModificationsAfterOptimizations); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 pipelineModificationsBeforeOptimizations.Additions.Add(RegisterStep.Create(stepd Id.ToString(), typeof(Throwing), "1", b => new Throwing())); pipelineModificationsAfterOptimizations.Additions.Add(RegisterStep.Create(stepdI d.ToString(), typeof(Throwing), "1", b => new Throwing())); [ShortRunJob] 1 [MemoryDiagnoser] 2 public class Step2_PipelineException { 3 [GlobalSetup] 4 public void SetUp() { 5 ... 6 var stepdId = PipelineDepth + 1; 7 8 9 ... 10 11 12 pipelineBeforeOptimizations = new Step1.PipelineOptimization<IBehaviorContext>(null, new SettingsHolder(), 13 pipelineModificationsBeforeOptimizations); 14 pipelineAfterOptimizations = new PipelineOptimization<IBehaviorContext> (null, new SettingsHolder(), 15 pipelineModificationsAfterOptimizations); 16 } 17 } 18  Benchmarking the pipeline
  • 40. [ShortRunJob] [MemoryDiagnoser] public class Step2_PipelineException { [GlobalSetup] public void SetUp() { ... } [Benchmark(Baseline = true)] public async Task Before() { try { await pipelineBeforeOptimizations.Invoke(behaviorContext); } catch (InvalidOperationException) { } } [Benchmark] public async Task After() { try { await pipelineAfterOptimizations.Invoke(behaviorContext); } catch (InvalidOperationException) { } } ... } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31  Benchmarking the pipeline
  • 43.  Profiling the pipeline (again) PUBLISH MEMORY CHARACTERISTICS BEFORE AFTER
  • 44. RECEIVE MEMORY CHARACTERISTICS  Profiling the pipeline (again) AFTER BEFORE
  • 45. MEMORY CHARACTERISTICS  Profiling the pipeline (again) RECEIVE AFTER BEFORE
  • 46. MEMORY CHARACTERISTICS  Profiling the pipeline (again) AFTER BEFORE
  • 51. THE HARNESS await using var serviceBusClient = new ServiceBusClient(connectionString); await using var sender = serviceBusClient.CreateSender(destination); var messages = new List<ServiceBusMessage>(1000); for (int i = 0; i < 1000; i++) { messages.Add(new ServiceBusMessage(UTF8.GetBytes($"Deep Dive {i} Deep Dive {i} Deep Dive {i} Deep Dive {i} Deep Dive {i} Deep Dive {i}"))); if (i % 100 == 0) { await sender.SendMessagesAsync(messages); messages.Clear(); } } await sender.SendMessagesAsync(messages); WriteLine("Messages sent"); Console.WriteLine("Take snapshot"); Console.ReadLine(); var countDownEvent = new CountdownEvent(1000); var processorOptions = new ServiceBusProcessorOptions { AutoCompleteMessages = true, MaxConcurrentCalls = 100, MaxAutoLockRenewalDuration = TimeSpan.FromMinutes(10), ReceiveMode = ServiceBusReceiveMode.PeekLock, }; await using var receiver = serviceBusClient.CreateProcessor(destination, processorOptions); receiver.ProcessMessageAsync += async messageEventArgs => { var message = messageEventArgs.Message; await Out.WriteLineAsync( $"Received message with '{message.MessageId}' and content '{UTF8.GetString(message.Body)}' / binary {message.Body}"); countDownEvent.Signal(); }; // rest omitted await receiver.StartProcessingAsync(); countDownEvent.Wait(); Console.WriteLine("Take snapshot"); Console.ReadLine(); await receiver.StopProcessingAsync(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 Getting lower on the stack
  • 52. THE HARNESS await using var serviceBusClient = new ServiceBusClient(connectionString); await using var sender = serviceBusClient.CreateSender(destination); var messages = new List<ServiceBusMessage>(1000); for (int i = 0; i < 1000; i++) { messages.Add(new ServiceBusMessage(UTF8.GetBytes($"Deep Dive {i} Deep Dive {i} Deep Dive {i} Deep Dive {i} Deep Dive {i} Deep Dive {i}"))); if (i % 100 == 0) { await sender.SendMessagesAsync(messages); messages.Clear(); } } await sender.SendMessagesAsync(messages); WriteLine("Messages sent"); Console.WriteLine("Take snapshot"); Console.ReadLine(); Getting lower on the stack
  • 53. THE HARNESS receiver.ProcessMessageAsync += async messageEventArgs => { var message = messageEventArgs.Message; await Out.WriteLineAsync( $"Received message with '{message.MessageId}' and content '{UTF8.GetString(message.Body)}' / binary {message.Body}"); countDownEvent.Signal(); }; var countDownEvent = new CountdownEvent(1000); 1 2 var processorOptions = new ServiceBusProcessorOptions 3 { 4 AutoCompleteMessages = true, 5 MaxConcurrentCalls = 100, 6 MaxAutoLockRenewalDuration = TimeSpan.FromMinutes(10), 7 ReceiveMode = ServiceBusReceiveMode.PeekLock, 8 }; 9 10 await using var receiver = serviceBusClient.CreateProcessor(destination, processorOptions); 11 12 13 14 15 16 17 // rest omitted 18 await receiver.StartProcessingAsync(); 19 20 countDownEvent.Wait(); 21 22 Console.WriteLine("Take snapshot"); 23 Console.ReadLine(); 24 25 await receiver.StopProcessingAsync(); 26 Getting lower on the stack
  • 56. PREVENTING REGRESSIONS C:ProjectsperformancesrctoolsResultsComparer> dotnet run --base "C:resultsbefore" --diff "C:resultsafter" --threshold 2% 1 2 Guidance Tool Preventing Regressions ResultComparer C:Projectsperformancesrcbenchmarksmicro> dotnet run -c Release -f net8.0 --artifacts "C:resultsbefore" 1 2 C:Projectsperformancesrcbenchmarksmicro> dotnet run -c Release -f net8.0 --artifacts "C:resultsafter" 1 2
  • 57. "Two subsequent builds on the same revision can have ranges of 1.5..2 seconds and 12..36 seconds. CPU-bound benchmarks are much more stable than Memory/Disk-bound benchmarks, but the “average” performance levels still can be up to three times different across builds." Andrey Akinshin - Performance stability of GitHub Actions
  • 58. BEYOND SIMPLE BENCHMARKS A PRACTICAL GUIDE TO OPTIMIZING CODE  | ✉ |  danielmarbach daniel.marbach@particular.net Daniel Marbach github.com/danielmarbach/BeyondSimpleBenchmarks Use the performance loop to improve your code where it matters Combine it with profiling to observe how the small changes add up Optimize until you hit a diminishing point of return You'll learn a ton about potential improvements for a new design