From 08467316769d9f76e716796d4146d83513dbcc2e Mon Sep 17 00:00:00 2001 From: Bartosz Sypytkowski Date: Mon, 28 May 2018 20:59:53 +0200 Subject: [PATCH 1/4] initial commit with BenchmarkDotNet benchmarks --- src/Akka.sln | 49 +++-- .../Actor/ActorPathBenchmarks.cs | 51 ++++++ .../Actor/AddressBenchmarks.cs | 57 ++++++ .../Actor/PingPongBenchmarks.cs | 114 ++++++++++++ .../Actor/SpawnActorBenchmarks.cs | 109 ++++++++++++ .../Akka.Benchmarks/Akka.Benchmarks.csproj | 28 +++ .../Akka.Benchmarks/Configurations/Configs.cs | 29 +++ .../IO/ByteStringBenchmarks.cs | 95 ++++++++++ src/benchmark/Akka.Benchmarks/Program.cs | 20 +++ .../Serialization/SerializationBenchmarks.cs | 76 ++++++++ .../Utils/ConsistentHashBenchmarks.cs | 168 ++++++++++++++++++ .../Utils/FastLazyBenchmarks.cs | 58 ++++++ .../Utils/TypeExtensionsBenchmarks.cs | 33 ++++ .../Akka.Streams/Properties/AssemblyInfo.cs | 1 + src/core/Akka/Properties/AssemblyInfo.cs | 1 + 15 files changed, 872 insertions(+), 17 deletions(-) create mode 100644 src/benchmark/Akka.Benchmarks/Actor/ActorPathBenchmarks.cs create mode 100644 src/benchmark/Akka.Benchmarks/Actor/AddressBenchmarks.cs create mode 100644 src/benchmark/Akka.Benchmarks/Actor/PingPongBenchmarks.cs create mode 100644 src/benchmark/Akka.Benchmarks/Actor/SpawnActorBenchmarks.cs create mode 100644 src/benchmark/Akka.Benchmarks/Akka.Benchmarks.csproj create mode 100644 src/benchmark/Akka.Benchmarks/Configurations/Configs.cs create mode 100644 src/benchmark/Akka.Benchmarks/IO/ByteStringBenchmarks.cs create mode 100644 src/benchmark/Akka.Benchmarks/Program.cs create mode 100644 src/benchmark/Akka.Benchmarks/Serialization/SerializationBenchmarks.cs create mode 100644 src/benchmark/Akka.Benchmarks/Utils/ConsistentHashBenchmarks.cs create mode 100644 src/benchmark/Akka.Benchmarks/Utils/FastLazyBenchmarks.cs create mode 100644 src/benchmark/Akka.Benchmarks/Utils/TypeExtensionsBenchmarks.cs diff --git a/src/Akka.sln b/src/Akka.sln index ac492335864..1df2814800f 100644 --- a/src/Akka.sln +++ b/src/Akka.sln @@ -164,8 +164,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.Cluster.Tests.Performa EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.DistributedData.LightningDB", "contrib\cluster\Akka.DistributedData.LightningDB\Akka.DistributedData.LightningDB.csproj", "{99CCB7CA-E1EE-4497-BF52-2200A0EC5CB1}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpawnBenchmark", "benchmark\SpawnBenchmark\SpawnBenchmark.csproj", "{DA4BDE1E-1ACA-482A-9329-952C3E36F97D}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RemotePingPong", "benchmark\RemotePingPong\RemotePingPong.csproj", "{5AA81B79-34DD-4DD7-9D40-A5CA389786DF}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{D3AF8295-AEB5-4324-AA82-FCC0014AC310}" @@ -188,9 +186,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samples.Cluster.Simple", "e EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samples.Cluster.Transformation", "examples\Cluster\Roles\Samples.Cluster.Transformation\Samples.Cluster.Transformation.csproj", "{01B074DB-67C8-4600-8CE2-DAAAFC052261}" EndProject -Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Akka.FSharp", "core/Akka.FSharp/Akka.FSharp.fsproj", "{483B4579-2665-49FC-A7E4-42C67FEC7531}" +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Akka.FSharp", "core\Akka.FSharp\Akka.FSharp.fsproj", "{483B4579-2665-49FC-A7E4-42C67FEC7531}" +EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Akka.FSharp.Tests", "core\Akka.FSharp.Tests\Akka.FSharp.Tests.fsproj", "{FA2E30CC-9F2D-4BFC-9899-347B32DF7181}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Benchmarks", "benchmark\Akka.Benchmarks\Akka.Benchmarks.csproj", "{A1D57384-A933-480A-9DF4-FA5E60AB1A67}" EndProject -Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Akka.FSharp.Tests", "core/Akka.FSharp.Tests/Akka.FSharp.Tests.fsproj", "{FA2E30CC-9F2D-4BFC-9899-347B32DF7181}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpawnBenchmark", "benchmark\SpawnBenchmark\SpawnBenchmark.csproj", "{9BEAF609-B406-4CCB-9708-6E8DFF764232}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -695,18 +697,6 @@ Global {99CCB7CA-E1EE-4497-BF52-2200A0EC5CB1}.Release|x64.Build.0 = Release|Any CPU {99CCB7CA-E1EE-4497-BF52-2200A0EC5CB1}.Release|x86.ActiveCfg = Release|Any CPU {99CCB7CA-E1EE-4497-BF52-2200A0EC5CB1}.Release|x86.Build.0 = Release|Any CPU - {DA4BDE1E-1ACA-482A-9329-952C3E36F97D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DA4BDE1E-1ACA-482A-9329-952C3E36F97D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DA4BDE1E-1ACA-482A-9329-952C3E36F97D}.Debug|x64.ActiveCfg = Debug|Any CPU - {DA4BDE1E-1ACA-482A-9329-952C3E36F97D}.Debug|x64.Build.0 = Debug|Any CPU - {DA4BDE1E-1ACA-482A-9329-952C3E36F97D}.Debug|x86.ActiveCfg = Debug|Any CPU - {DA4BDE1E-1ACA-482A-9329-952C3E36F97D}.Debug|x86.Build.0 = Debug|Any CPU - {DA4BDE1E-1ACA-482A-9329-952C3E36F97D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DA4BDE1E-1ACA-482A-9329-952C3E36F97D}.Release|Any CPU.Build.0 = Release|Any CPU - {DA4BDE1E-1ACA-482A-9329-952C3E36F97D}.Release|x64.ActiveCfg = Release|Any CPU - {DA4BDE1E-1ACA-482A-9329-952C3E36F97D}.Release|x64.Build.0 = Release|Any CPU - {DA4BDE1E-1ACA-482A-9329-952C3E36F97D}.Release|x86.ActiveCfg = Release|Any CPU - {DA4BDE1E-1ACA-482A-9329-952C3E36F97D}.Release|x86.Build.0 = Release|Any CPU {5AA81B79-34DD-4DD7-9D40-A5CA389786DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5AA81B79-34DD-4DD7-9D40-A5CA389786DF}.Debug|Any CPU.Build.0 = Debug|Any CPU {5AA81B79-34DD-4DD7-9D40-A5CA389786DF}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -815,6 +805,30 @@ Global {FA2E30CC-9F2D-4BFC-9899-347B32DF7181}.Release|x64.Build.0 = Release|Any CPU {FA2E30CC-9F2D-4BFC-9899-347B32DF7181}.Release|x86.ActiveCfg = Release|Any CPU {FA2E30CC-9F2D-4BFC-9899-347B32DF7181}.Release|x86.Build.0 = Release|Any CPU + {A1D57384-A933-480A-9DF4-FA5E60AB1A67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1D57384-A933-480A-9DF4-FA5E60AB1A67}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1D57384-A933-480A-9DF4-FA5E60AB1A67}.Debug|x64.ActiveCfg = Debug|Any CPU + {A1D57384-A933-480A-9DF4-FA5E60AB1A67}.Debug|x64.Build.0 = Debug|Any CPU + {A1D57384-A933-480A-9DF4-FA5E60AB1A67}.Debug|x86.ActiveCfg = Debug|Any CPU + {A1D57384-A933-480A-9DF4-FA5E60AB1A67}.Debug|x86.Build.0 = Debug|Any CPU + {A1D57384-A933-480A-9DF4-FA5E60AB1A67}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1D57384-A933-480A-9DF4-FA5E60AB1A67}.Release|Any CPU.Build.0 = Release|Any CPU + {A1D57384-A933-480A-9DF4-FA5E60AB1A67}.Release|x64.ActiveCfg = Release|Any CPU + {A1D57384-A933-480A-9DF4-FA5E60AB1A67}.Release|x64.Build.0 = Release|Any CPU + {A1D57384-A933-480A-9DF4-FA5E60AB1A67}.Release|x86.ActiveCfg = Release|Any CPU + {A1D57384-A933-480A-9DF4-FA5E60AB1A67}.Release|x86.Build.0 = Release|Any CPU + {9BEAF609-B406-4CCB-9708-6E8DFF764232}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9BEAF609-B406-4CCB-9708-6E8DFF764232}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9BEAF609-B406-4CCB-9708-6E8DFF764232}.Debug|x64.ActiveCfg = Debug|Any CPU + {9BEAF609-B406-4CCB-9708-6E8DFF764232}.Debug|x64.Build.0 = Debug|Any CPU + {9BEAF609-B406-4CCB-9708-6E8DFF764232}.Debug|x86.ActiveCfg = Debug|Any CPU + {9BEAF609-B406-4CCB-9708-6E8DFF764232}.Debug|x86.Build.0 = Debug|Any CPU + {9BEAF609-B406-4CCB-9708-6E8DFF764232}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9BEAF609-B406-4CCB-9708-6E8DFF764232}.Release|Any CPU.Build.0 = Release|Any CPU + {9BEAF609-B406-4CCB-9708-6E8DFF764232}.Release|x64.ActiveCfg = Release|Any CPU + {9BEAF609-B406-4CCB-9708-6E8DFF764232}.Release|x64.Build.0 = Release|Any CPU + {9BEAF609-B406-4CCB-9708-6E8DFF764232}.Release|x86.ActiveCfg = Release|Any CPU + {9BEAF609-B406-4CCB-9708-6E8DFF764232}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -885,7 +899,6 @@ Global {E7A836C1-42D3-4ADC-81C6-C6C5398CC740} = {7625FD95-4B2C-4A5B-BDD5-94B1493FAC8E} {94C10096-C33E-404E-B0FB-F8535512A952} = {01167D3C-49C4-4CDE-9787-C176D139ACDD} {99CCB7CA-E1EE-4497-BF52-2200A0EC5CB1} = {76F58DC4-19F1-43EF-A6E2-EC1CC8395AC5} - {DA4BDE1E-1ACA-482A-9329-952C3E36F97D} = {73108242-625A-4D7B-AA09-63375DBAE464} {5AA81B79-34DD-4DD7-9D40-A5CA389786DF} = {73108242-625A-4D7B-AA09-63375DBAE464} {C50E1A9E-820C-4E75-AE39-6F96A99AC4A7} = {D3AF8295-AEB5-4324-AA82-FCC0014AC310} {820CFBF7-E165-4ADA-B821-E6A0A81DFE84} = {C50E1A9E-820C-4E75-AE39-6F96A99AC4A7} @@ -898,6 +911,8 @@ Global {01B074DB-67C8-4600-8CE2-DAAAFC052261} = {C50E1A9E-820C-4E75-AE39-6F96A99AC4A7} {483B4579-2665-49FC-A7E4-42C67FEC7531} = {01167D3C-49C4-4CDE-9787-C176D139ACDD} {FA2E30CC-9F2D-4BFC-9899-347B32DF7181} = {01167D3C-49C4-4CDE-9787-C176D139ACDD} + {A1D57384-A933-480A-9DF4-FA5E60AB1A67} = {73108242-625A-4D7B-AA09-63375DBAE464} + {9BEAF609-B406-4CCB-9708-6E8DFF764232} = {73108242-625A-4D7B-AA09-63375DBAE464} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {03AD8E21-7507-4E68-A4E9-F4A7E7273164} diff --git a/src/benchmark/Akka.Benchmarks/Actor/ActorPathBenchmarks.cs b/src/benchmark/Akka.Benchmarks/Actor/ActorPathBenchmarks.cs new file mode 100644 index 00000000000..c22ba44a856 --- /dev/null +++ b/src/benchmark/Akka.Benchmarks/Actor/ActorPathBenchmarks.cs @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2018 Lightbend Inc. +// Copyright (C) 2013-2018 .NET Foundation +// +//----------------------------------------------------------------------- + +using Akka.Actor; +using Akka.Benchmarks.Configurations; +using BenchmarkDotNet.Attributes; + +namespace Akka.Benchmarks.Actor +{ + [Config(typeof(MicroBenchmarkConfig))] + public class ActorPathBenchmarks + { + private ActorPath x; + private ActorPath y; + + [GlobalSetup] + public void Setup() + { + x = new RootActorPath(new Address("akka.tcp", "system", "127.0.0.1", 1337), "user"); + y = new RootActorPath(new Address("akka.tcp", "system", "127.0.0.1", 1337), "system"); + } + + [Benchmark] + public ActorPath ActorPath_Parse() + { + return ActorPath.Parse("akka.tcp://system/user/parent/child"); + } + + [Benchmark] + public ActorPath ActorPath_Concat() + { + return x / "parent" / "child"; + } + + [Benchmark] + public bool ActorPath_Equals() + { + return x == y; + } + + [Benchmark] + public string ActorPath_ToString() + { + return x.ToString(); + } + } +} \ No newline at end of file diff --git a/src/benchmark/Akka.Benchmarks/Actor/AddressBenchmarks.cs b/src/benchmark/Akka.Benchmarks/Actor/AddressBenchmarks.cs new file mode 100644 index 00000000000..b3125cef350 --- /dev/null +++ b/src/benchmark/Akka.Benchmarks/Actor/AddressBenchmarks.cs @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2018 Lightbend Inc. +// Copyright (C) 2013-2018 .NET Foundation +// +//----------------------------------------------------------------------- + +using Akka.Actor; +using Akka.Benchmarks.Configurations; +using BenchmarkDotNet.Attributes; + +namespace Akka.Benchmarks.Actor +{ + [Config(typeof(MicroBenchmarkConfig))] + public class AddressBenchmarks + { + private Address x; + private Address y; + + [GlobalSetup] + public void Setup() + { + x = new Address("akka.tcp", "test-system", "10.62.0.101", 4000); + y = new Address("akka.tcp", "test-system", "10.62.0.101", 4123); + } + + [Benchmark] + public Address Address_Parse() + { + return Address.Parse("akka.tcp://test-system@10.62.0.100:5000/"); + } + + [Benchmark] + public int Address_CompareTo() + { + return x.CompareTo(y); + } + + [Benchmark] + public string Address_ToString() + { + return x.ToString(); + } + + [Benchmark] + public bool Address_Equals() + { + return x == y; + } + + [Benchmark] + public int Address_GetHashCode() + { + return x.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/src/benchmark/Akka.Benchmarks/Actor/PingPongBenchmarks.cs b/src/benchmark/Akka.Benchmarks/Actor/PingPongBenchmarks.cs new file mode 100644 index 00000000000..41a6a9db6e9 --- /dev/null +++ b/src/benchmark/Akka.Benchmarks/Actor/PingPongBenchmarks.cs @@ -0,0 +1,114 @@ +// //----------------------------------------------------------------------- +// +// Copyright (C) 2009-2018 Lightbend Inc. +// Copyright (C) 2013-2018 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Threading.Tasks; +using Akka.Actor; +using Akka.Benchmarks.Configurations; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes.Jobs; +using BenchmarkDotNet.Engines; + +namespace Akka.Benchmarks.Actor +{ + [Config(typeof(MonitoringConfig))] + [SimpleJob(RunStrategy.Monitoring, launchCount: 3, warmupCount: 3, targetCount: 3)] + public class PingPongBenchmarks + { + public const int Operations = 1_000_000; + private TimeSpan timeout; + private ActorSystem system; + private IActorRef ping; + + [GlobalSetup] + public void Setup() + { + timeout = TimeSpan.FromMinutes(1); + system = ActorSystem.Create("system"); + var pong = system.ActorOf(Props.Create(() => new Pong())); + ping = system.ActorOf(Props.Create(() => new Ping(pong))); + } + + [GlobalCleanup] + public void Cleanup() + { + system.Dispose(); + } + + [Benchmark(OperationsPerInvoke = Operations)] + public async Task Actor_ping_pong_single_pair_in_memory() + { + await ping.Ask(StartTest.Instance, timeout); + } + + #region actors + + sealed class StartTest + { + public static readonly StartTest Instance = new StartTest(); + private StartTest() { } + } + + sealed class Signal + { + public int Remaining { get; } + + public Signal(int remaining) + { + Remaining = remaining; + } + } + + sealed class TestDone + { + public static readonly TestDone Instance = new TestDone(); + private TestDone() { } + } + + sealed class Ping : ReceiveActor + { + private IActorRef replyTo; + + public Ping(IActorRef pong) + { + Receive(_ => + { + replyTo = Sender; + + var signal = new Signal(Operations); + pong.Tell(signal); + }); + + Receive(signal => + { + var remaining = signal.Remaining; + if (remaining <= 0) + { + replyTo.Tell(TestDone.Instance); + } + else + { + Sender.Tell(new Signal(remaining - 1)); + } + }); + } + } + sealed class Pong : ReceiveActor + { + public Pong() + { + Receive(signal => + { + Sender.Tell(new Signal(signal.Remaining - 1)); + }); + } + } + + #endregion + + } +} \ No newline at end of file diff --git a/src/benchmark/Akka.Benchmarks/Actor/SpawnActorBenchmarks.cs b/src/benchmark/Akka.Benchmarks/Actor/SpawnActorBenchmarks.cs new file mode 100644 index 00000000000..4b8def886b2 --- /dev/null +++ b/src/benchmark/Akka.Benchmarks/Actor/SpawnActorBenchmarks.cs @@ -0,0 +1,109 @@ +// //----------------------------------------------------------------------- +// +// Copyright (C) 2009-2018 Lightbend Inc. +// Copyright (C) 2013-2018 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Threading.Tasks; +using Akka.Actor; +using Akka.Benchmarks.Configurations; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes.Jobs; +using BenchmarkDotNet.Engines; + +namespace Akka.Benchmarks.Actor +{ + [Config(typeof(MonitoringConfig))] + [SimpleJob(RunStrategy.Monitoring, launchCount: 1, warmupCount: 1, targetCount: 1)] + public class SpawnActorBenchmarks + { + public const int ActorCount = 100_000; + private TimeSpan timeout; + private ActorSystem system; + + [GlobalSetup] + public void Setup() + { + timeout = TimeSpan.FromMinutes(1); + system = ActorSystem.Create("system"); + } + + [GlobalCleanup] + public void Cleanup() + { + system.Dispose(); + } + + [Benchmark(OperationsPerInvoke = ActorCount)] + public async Task Actor_spawn() + { + var parent = system.ActorOf(Parent.Props); + await parent.Ask(StartTest.Instance, timeout); + } + + #region actors + + sealed class StartTest + { + public static readonly StartTest Instance = new StartTest(); + private StartTest() { } + } + + sealed class ChildReady + { + public static readonly ChildReady Instance = new ChildReady(); + private ChildReady() { } + } + + sealed class TestDone + { + public static readonly TestDone Instance = new TestDone(); + private TestDone() { } + } + + sealed class Parent : ReceiveActor + { + public static readonly Props Props = Props.Create(); + private int count = ActorCount - 1; // -1 because we also create the parent + private IActorRef replyTo; + public Parent() + { + Receive(_ => + { + replyTo = Sender; + for (int i = 0; i < count; i++) + { + Context.ActorOf(Child.Props); + } + }); + Receive(_ => + { + count--; + if (count == 0) + { + replyTo.Tell(TestDone.Instance); + } + }); + } + } + + sealed class Child : ReceiveActor + { + public static readonly Props Props = Props.Create(); + public Child() + { + ReceiveAny(_ => {}); + } + + protected override void PreStart() + { + base.PreStart(); + Context.Parent.Tell(ChildReady.Instance); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/benchmark/Akka.Benchmarks/Akka.Benchmarks.csproj b/src/benchmark/Akka.Benchmarks/Akka.Benchmarks.csproj new file mode 100644 index 00000000000..97a059c015c --- /dev/null +++ b/src/benchmark/Akka.Benchmarks/Akka.Benchmarks.csproj @@ -0,0 +1,28 @@ + + + + Exe + netcoreapp2.0 + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/benchmark/Akka.Benchmarks/Configurations/Configs.cs b/src/benchmark/Akka.Benchmarks/Configurations/Configs.cs new file mode 100644 index 00000000000..eb1fb412577 --- /dev/null +++ b/src/benchmark/Akka.Benchmarks/Configurations/Configs.cs @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2018 Lightbend Inc. +// Copyright (C) 2013-2018 .NET Foundation +// +//----------------------------------------------------------------------- + +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Exporters; + +namespace Akka.Benchmarks.Configurations +{ + public class MicroBenchmarkConfig : ManualConfig + { + public MicroBenchmarkConfig() + { + this.Add(new MemoryDiagnoser()); + this.Add(MarkdownExporter.GitHub); + } + } + public class MonitoringConfig : ManualConfig + { + public MonitoringConfig() + { + this.Add(MarkdownExporter.GitHub); + } + } +} \ No newline at end of file diff --git a/src/benchmark/Akka.Benchmarks/IO/ByteStringBenchmarks.cs b/src/benchmark/Akka.Benchmarks/IO/ByteStringBenchmarks.cs new file mode 100644 index 00000000000..2c4e50f277f --- /dev/null +++ b/src/benchmark/Akka.Benchmarks/IO/ByteStringBenchmarks.cs @@ -0,0 +1,95 @@ +// //----------------------------------------------------------------------- +// +// Copyright (C) 2009-2018 Lightbend Inc. +// Copyright (C) 2013-2018 .NET Foundation +// +//----------------------------------------------------------------------- + +using Akka.Benchmarks.Configurations; +using Akka.IO; +using BenchmarkDotNet.Attributes; + +namespace Akka.Benchmarks +{ + [Config(typeof(MicroBenchmarkConfig))] + public class ByteStringBenchmarks + { + [Params(10, 100, 1000)] + public int PayloadSize; + + private byte[] _bytes; + private string _str; + + private ByteString _multipart; + + [GlobalSetup] + public void Setup() + { + _bytes = new byte[PayloadSize]; + _str = new string('x', PayloadSize); + + _multipart = ByteString.Empty; + byte acc = 1; + for (int i = 0; i < 10; i++) + { + var array = new byte[i]; + for (int j = 0; j < i; j++) + { + array[j] = (byte)(acc++ % byte.MaxValue); + } + _multipart += ByteString.FromBytes(array); + } + } + + [Benchmark] + public ByteString ByteString_create_unsafe() + { + return ByteString.FromBytes(_bytes); + } + + [Benchmark] + public ByteString ByteString_create_copying() + { + return ByteString.CopyFrom(_bytes); + } + + [Benchmark] + public ByteString ByteString_create_from_string() + { + return ByteString.FromString(_str); + } + + [Benchmark] + [Arguments(10)] + public ByteString ByteString_concatenation(int times) + { + var acc = ByteString.Empty; + for (int i = 0; i < times; i++) + { + acc += ByteString.FromBytes(_bytes); + } + + return acc; + } + + [Benchmark] + public ByteString ByteString_multipart_slice() + { + return _multipart.Slice(10, 40); + } + + [Benchmark] + public ByteString ByteString_multipart_compact() + { + return _multipart.Compact(); + } + + + [Benchmark] + public bool ByteString_multipart_has_substring() + { + byte[] array = { 6, 7, 8, 9 }; + return _multipart.HasSubstring(ByteString.FromBytes(array), 0); + } + } +} \ No newline at end of file diff --git a/src/benchmark/Akka.Benchmarks/Program.cs b/src/benchmark/Akka.Benchmarks/Program.cs new file mode 100644 index 00000000000..ec0fda24222 --- /dev/null +++ b/src/benchmark/Akka.Benchmarks/Program.cs @@ -0,0 +1,20 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2018 Lightbend Inc. +// Copyright (C) 2013-2018 .NET Foundation +// +//----------------------------------------------------------------------- +using System; +using System.Reflection; +using BenchmarkDotNet.Running; + +namespace Akka.Benchmarks +{ + class Program + { + static void Main(string[] args) + { + BenchmarkSwitcher.FromAssembly(Assembly.GetExecutingAssembly()).Run(args); + } + } +} diff --git a/src/benchmark/Akka.Benchmarks/Serialization/SerializationBenchmarks.cs b/src/benchmark/Akka.Benchmarks/Serialization/SerializationBenchmarks.cs new file mode 100644 index 00000000000..723f9b07944 --- /dev/null +++ b/src/benchmark/Akka.Benchmarks/Serialization/SerializationBenchmarks.cs @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2018 Lightbend Inc. +// Copyright (C) 2013-2018 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using Akka.Actor; +using Akka.Benchmarks.Configurations; +using Akka.Serialization; +using BenchmarkDotNet.Attributes; + +namespace Akka.Benchmarks.Serialization +{ + [Config(typeof(MicroBenchmarkConfig))] + public class SerializationBenchmarks + { + public const int Operations = 10_000; + + private ExtendedActorSystem system; + private NewtonSoftJsonSerializer json; + private Message message; + + [GlobalSetup] + public void Setup() + { + system = (ExtendedActorSystem)ActorSystem.Create("system"); + json = new NewtonSoftJsonSerializer(system); + var actorRef = system.ActorOf(); + message = new Message(123, Guid.NewGuid().ToString("D"), actorRef); + } + + [GlobalCleanup] + public void Cleanup() + { + system.Dispose(); + } + + [Benchmark(OperationsPerInvoke = Operations)] + public void Json_serializer_message_roundtrip() + { + for (int i = 0; i < Operations; i++) + { + var bytes = json.ToBinary(message); + json.FromBinary(bytes, typeof(Message)); + } + } + + #region messages + + sealed class Echo : ReceiveActor + { + public Echo() + { + ReceiveAny(msg => Sender.Tell(msg)); + } + } + + public sealed class Message + { + public int Id { get; } + public string Name { get; } + public IActorRef ActorRef { get; } + + public Message(int id, string name, IActorRef actorRef) + { + Id = id; + Name = name; + ActorRef = actorRef; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/benchmark/Akka.Benchmarks/Utils/ConsistentHashBenchmarks.cs b/src/benchmark/Akka.Benchmarks/Utils/ConsistentHashBenchmarks.cs new file mode 100644 index 00000000000..bd7b90adeb8 --- /dev/null +++ b/src/benchmark/Akka.Benchmarks/Utils/ConsistentHashBenchmarks.cs @@ -0,0 +1,168 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2018 Lightbend Inc. +// Copyright (C) 2013-2018 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Text; +using Akka.Benchmarks.Configurations; +using Akka.Util; +using BenchmarkDotNet.Attributes; + +namespace Akka.Benchmarks.Utils +{ + [Config(typeof(MicroBenchmarkConfig))] + public class ConsistentHashBenchmarks + { + + private string testString; + private byte[] testBinary; + + [GlobalSetup] + public void Setup() + { + testString = Guid.NewGuid().ToString("D"); + testBinary = new byte[100]; + + ThreadLocalRandom.Current.NextBytes(testBinary); + } + + [Benchmark] + public int Murmur_string_hash() + { + return MurmurHash.StringHash(testString); + } + + [Benchmark] + public uint Jenkins_string_hash() + { + return Jenkins.Hash(testString); + } + + [Benchmark] + public int Murmur_binary_hash() + { + return MurmurHash.ByteHash(testBinary); + } + + [Benchmark] + public uint Jenkins_binary_hash() + { + return Jenkins.Hash(testBinary); + } + + #region Jenkins + + #region copyright + // dotnet/orleans is licensed under the MIT License + // + // A short and simple permissive license with conditions only requiring + // preservation of copyright and license notices. Licensed works, + // modifications, and larger works may be distributed under different + // terms and without source code. + #endregion + + public static class Jenkins + { + private static void Mix(ref uint a, ref uint b, ref uint c) + { + a -= b; a -= c; a ^= (c >> 13); + b -= c; b -= a; b ^= (a << 8); + c -= a; c -= b; c ^= (b >> 13); + a -= b; a -= c; a ^= (c >> 12); + b -= c; b -= a; b ^= (a << 16); + c -= a; c -= b; c ^= (b >> 5); + a -= b; a -= c; a ^= (c >> 3); + b -= c; b -= a; b ^= (a << 10); + c -= a; c -= b; c ^= (b >> 15); + } + + // This is the reference implementation of the Jenkins hash. + public static uint Hash(byte[] data) + { + int len = data.Length; + uint a = 0x9e3779b9; + uint b = a; + uint c = 0; + int i = 0; + + while (i + 12 <= len) + { + a += (uint)data[i++] | + ((uint)data[i++] << 8) | + ((uint)data[i++] << 16) | + ((uint)data[i++] << 24); + b += (uint)data[i++] | + ((uint)data[i++] << 8) | + ((uint)data[i++] << 16) | + ((uint)data[i++] << 24); + c += (uint)data[i++] | + ((uint)data[i++] << 8) | + ((uint)data[i++] << 16) | + ((uint)data[i++] << 24); + Mix(ref a, ref b, ref c); + } + c += (uint)len; + if (i < len) + a += data[i++]; + if (i < len) + a += (uint)data[i++] << 8; + if (i < len) + a += (uint)data[i++] << 16; + if (i < len) + a += (uint)data[i++] << 24; + if (i < len) + b += (uint)data[i++]; + if (i < len) + b += (uint)data[i++] << 8; + if (i < len) + b += (uint)data[i++] << 16; + if (i < len) + b += (uint)data[i++] << 24; + if (i < len) + c += (uint)data[i++] << 8; + if (i < len) + c += (uint)data[i++] << 16; + if (i < len) + c += (uint)data[i++] << 24; + Mix(ref a, ref b, ref c); + return c; + } + + public static uint Hash(string data) + { + byte[] bytesToHash = Encoding.UTF8.GetBytes(data); + return Hash(bytesToHash); + } + + // This implementation calculates the exact same hash value as the above, but is + // optimized for the case where the input is exactly 24 bytes of data provided as + // three 8-byte unsigned integers. + public static uint Hash(ulong u1, ulong u2, ulong u3) + { + uint a = 0x9e3779b9; + uint b = a; + uint c = 0; + + unchecked + { + a += (uint)u1; + b += (uint)((u1 ^ (uint)u1) >> 32); + c += (uint)u2; + Mix(ref a, ref b, ref c); + a += (uint)((u2 ^ (uint)u2) >> 32); + b += (uint)u3; + c += (uint)((u3 ^ (uint)u3) >> 32); + } + Mix(ref a, ref b, ref c); + c += 24; + Mix(ref a, ref b, ref c); + return c; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/benchmark/Akka.Benchmarks/Utils/FastLazyBenchmarks.cs b/src/benchmark/Akka.Benchmarks/Utils/FastLazyBenchmarks.cs new file mode 100644 index 00000000000..03b8483d984 --- /dev/null +++ b/src/benchmark/Akka.Benchmarks/Utils/FastLazyBenchmarks.cs @@ -0,0 +1,58 @@ +// //----------------------------------------------------------------------- +// +// Copyright (C) 2009-2018 Lightbend Inc. +// Copyright (C) 2013-2018 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Threading; +using Akka.Benchmarks.Configurations; +using Akka.Util; +using BenchmarkDotNet.Attributes; + +namespace Akka.Benchmarks.Utils +{ + [Config(typeof(MicroBenchmarkConfig))] + public class FastLazyBenchmarks + { + private Lazy lazySafe; + private Lazy lazyUnsafe; + private FastLazy fastLazy; + private FastLazy fastLazyWithInit; + + + [GlobalSetup] + public void Setup() + { + lazySafe = new Lazy(() => 100, LazyThreadSafetyMode.ExecutionAndPublication); + lazyUnsafe = new Lazy(() => 100, LazyThreadSafetyMode.None); + fastLazy = new FastLazy(() => 100); + fastLazyWithInit = new FastLazy(state => state + 100, 1000); + } + + [Benchmark(Baseline = true)] + public int Lazy_safe_get_value() + { + return lazySafe.Value; + } + + [Benchmark] + public int Lazy_unsafe_get_value() + { + return lazyUnsafe.Value; + } + + [Benchmark] + public int FastLazy_get_value() + { + return fastLazy.Value; + } + + [Benchmark] + public int FastLazy_satefull_get_value() + { + return fastLazyWithInit.Value; + } + } +} \ No newline at end of file diff --git a/src/benchmark/Akka.Benchmarks/Utils/TypeExtensionsBenchmarks.cs b/src/benchmark/Akka.Benchmarks/Utils/TypeExtensionsBenchmarks.cs new file mode 100644 index 00000000000..139c73751f3 --- /dev/null +++ b/src/benchmark/Akka.Benchmarks/Utils/TypeExtensionsBenchmarks.cs @@ -0,0 +1,33 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2018 Lightbend Inc. +// Copyright (C) 2013-2018 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using Akka.Benchmarks.Configurations; +using Akka.Streams.Actors; +using Akka.Util; +using BenchmarkDotNet.Attributes; + +namespace Akka.Benchmarks.Utils +{ + [Config(typeof(MicroBenchmarkConfig))] + public class TypeExtensionsBenchmarks + { + private static readonly Type type = typeof(Subscribe); + + [Benchmark(Baseline = true)] + public string Type_FullName() + { + return type.FullName; + } + + [Benchmark] + public string Type_QualifiedName() + { + return type.TypeQualifiedName(); + } + } +} \ No newline at end of file diff --git a/src/core/Akka.Streams/Properties/AssemblyInfo.cs b/src/core/Akka.Streams/Properties/AssemblyInfo.cs index 96071b498bb..0b1aaa916a6 100644 --- a/src/core/Akka.Streams/Properties/AssemblyInfo.cs +++ b/src/core/Akka.Streams/Properties/AssemblyInfo.cs @@ -14,6 +14,7 @@ // associated with an assembly. [assembly: InternalsVisibleTo("Akka.Streams.Tests")] [assembly: InternalsVisibleTo("Akka.Streams.TestKit")] +[assembly: InternalsVisibleTo("Akka.Benchmarks")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from diff --git a/src/core/Akka/Properties/AssemblyInfo.cs b/src/core/Akka/Properties/AssemblyInfo.cs index bff4045b8eb..ddf895073e6 100644 --- a/src/core/Akka/Properties/AssemblyInfo.cs +++ b/src/core/Akka/Properties/AssemblyInfo.cs @@ -37,3 +37,4 @@ [assembly: InternalsVisibleTo("Akka.Cluster.Tools")] [assembly: InternalsVisibleTo("Akka.Persistence")] [assembly: InternalsVisibleTo("Akka.Streams")] +[assembly: InternalsVisibleTo("Akka.Benchmarks")] From 00ec88f0dfd3a745d7c37f1ed7a1127fc4c635ed Mon Sep 17 00:00:00 2001 From: Bartosz Sypytkowski Date: Mon, 28 May 2018 21:09:08 +0200 Subject: [PATCH 2/4] benchmarkd config comments --- src/benchmark/Akka.Benchmarks/Configurations/Configs.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/benchmark/Akka.Benchmarks/Configurations/Configs.cs b/src/benchmark/Akka.Benchmarks/Configurations/Configs.cs index eb1fb412577..24e56c7cd6b 100644 --- a/src/benchmark/Akka.Benchmarks/Configurations/Configs.cs +++ b/src/benchmark/Akka.Benchmarks/Configurations/Configs.cs @@ -11,6 +11,9 @@ namespace Akka.Benchmarks.Configurations { + /// + /// Basic BenchmarkDotNet configuration used for microbenchmarks. + /// public class MicroBenchmarkConfig : ManualConfig { public MicroBenchmarkConfig() @@ -19,6 +22,10 @@ public MicroBenchmarkConfig() this.Add(MarkdownExporter.GitHub); } } + + /// + /// BenchmarkDotNet configuration used for monitored jobs (not for microbenchmarks). + /// public class MonitoringConfig : ManualConfig { public MonitoringConfig() From 914e62b5ce7734be4c08d9b292ca6e763723d03f Mon Sep 17 00:00:00 2001 From: Bartosz Sypytkowski Date: Mon, 28 May 2018 23:09:11 +0200 Subject: [PATCH 3/4] simple stream materialization + hocon benchmarks --- .../Akka.Benchmarks/Akka.Benchmarks.csproj | 4 - .../Akka.Benchmarks/Hocon/HoconBenchmarks.cs | 122 ++++++++++++++++++ .../Streams/MaterializationBenchmarks.cs | 49 +++++++ 3 files changed, 171 insertions(+), 4 deletions(-) create mode 100644 src/benchmark/Akka.Benchmarks/Hocon/HoconBenchmarks.cs create mode 100644 src/benchmark/Akka.Benchmarks/Streams/MaterializationBenchmarks.cs diff --git a/src/benchmark/Akka.Benchmarks/Akka.Benchmarks.csproj b/src/benchmark/Akka.Benchmarks/Akka.Benchmarks.csproj index 97a059c015c..8e84aa80eff 100644 --- a/src/benchmark/Akka.Benchmarks/Akka.Benchmarks.csproj +++ b/src/benchmark/Akka.Benchmarks/Akka.Benchmarks.csproj @@ -21,8 +21,4 @@ - - - - diff --git a/src/benchmark/Akka.Benchmarks/Hocon/HoconBenchmarks.cs b/src/benchmark/Akka.Benchmarks/Hocon/HoconBenchmarks.cs new file mode 100644 index 00000000000..183d2359e0c --- /dev/null +++ b/src/benchmark/Akka.Benchmarks/Hocon/HoconBenchmarks.cs @@ -0,0 +1,122 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2018 Lightbend Inc. +// Copyright (C) 2013-2018 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using Akka.Benchmarks.Configurations; +using Akka.Configuration; +using BenchmarkDotNet.Attributes; + +namespace Akka.Benchmarks.Hocon +{ + [Config(typeof(MicroBenchmarkConfig))] + public class HoconBenchmarks + { + public const string hoconString = @" + akka { + actor { + provider = remote + deployment { + /remoteecho { + remote = ""akka.tcp://DeployTarget@localhost:8090"" + } + } + } + remote { + dot-netty.tcp { + port = 8090 + hostname = localhost + } + } + persistence { + journal { + plugin = akka.persistence.journal.in-memory + } + } + } + "; + + public Config fallback1; + public Config fallback2; + public Config fallback3; + + [GlobalSetup] + public void Setup() + { + fallback1 = ConfigurationFactory.ParseString(@" + akka.actor.ask-timeout = 60s + akka.actor.branch-factor = 0.25 + akka.persistence { + journal.plugin = akka.persistence.journal.sql-server + journal.sql-server { + auto-initialize = on + } + }"); + fallback2 = ConfigurationFactory.ParseString(@" + akka.actor.provider = cluster + akka.remote.dot-netty.tcp { + hostname = ""127.0.0.1"" + port = 0 + } + akka.cluster { + seed-nodes = [ + ""akka.tcp://system@localhost:8000/"", + ""akka.tcp://system@localhost:8001/"" + ] + }"); + fallback3 = Persistence.Persistence.DefaultConfig(); + } + + [Benchmark] + public string Hocon_parse_resolve_string_value() + { + return fallback2.GetString("akka.actor.provider"); + } + + [Benchmark] + public int Hocon_parse_resolve_int_value() + { + return fallback2.GetInt("akka.remote.dot-netty.tcp.port"); + } + + [Benchmark] + public double Hocon_parse_resolve_double_value() + { + return fallback1.GetDouble("akka.actor.branch-factor"); + } + + [Benchmark] + public bool Hocon_parse_resolve_boolean_value() + { + return fallback1.GetBoolean("akka.persistence.journal.sql-server.auto-initialize"); + } + + [Benchmark] + public TimeSpan Hocon_parse_resolve_TimeSpan_value() + { + return fallback1.GetTimeSpan("akka.actor.ask-timeout"); + } + + [Benchmark] + public IEnumerable Hocon_parse_resolve_string_list_value() + { + return fallback1.GetStringList("akka.cluster.seed-nodes"); + } + + [Benchmark] + public Config Hocon_parse_raw() + { + return ConfigurationFactory.ParseString(hoconString); + } + + [Benchmark] + public Config Hocon_combine_fallbacks() + { + return fallback1.WithFallback(fallback2).WithFallback(fallback3); + } + } +} \ No newline at end of file diff --git a/src/benchmark/Akka.Benchmarks/Streams/MaterializationBenchmarks.cs b/src/benchmark/Akka.Benchmarks/Streams/MaterializationBenchmarks.cs new file mode 100644 index 00000000000..5e558b0c941 --- /dev/null +++ b/src/benchmark/Akka.Benchmarks/Streams/MaterializationBenchmarks.cs @@ -0,0 +1,49 @@ +// //----------------------------------------------------------------------- +// // +// // Copyright (C) 2009-2018 Lightbend Inc. +// // Copyright (C) 2013-2018 .NET Foundation +// // +// //----------------------------------------------------------------------- + +using System.Threading.Tasks; +using Akka.Actor; +using Akka.Benchmarks.Configurations; +using Akka.Streams; +using Akka.Streams.Dsl; +using BenchmarkDotNet.Attributes; + +namespace Akka.Benchmarks.Streams +{ + [Config(typeof(MicroBenchmarkConfig))] + public class MaterializationBenchmarks + { + private ActorSystem system; + private ActorMaterializer materializer; + + private IRunnableGraph simpleGraph; + + [GlobalSetup] + public void Setup() + { + system = ActorSystem.Create("system"); + materializer = system.Materializer(); + + simpleGraph = Source.Single(1) + .Select(x => x + 1) + .ToMaterialized(Sink.ForEach(i => { }), Keep.Right); + } + + [GlobalCleanup] + public void Cleanup() + { + materializer.Dispose(); + system.Dispose(); + } + + [Benchmark] + public void Actor_materializer_run_simple_linear_graph() + { + simpleGraph.Run(materializer); + } + } +} \ No newline at end of file From 0fd5ebff6a6a915b3178a9527c9e21034121ef3f Mon Sep 17 00:00:00 2001 From: Bartosz Sypytkowski Date: Wed, 30 May 2018 19:28:33 +0200 Subject: [PATCH 4/4] API approvals update --- src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt | 3 ++- .../Akka.API.Tests/CoreAPISpec.ApproveStreams.approved.txt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt index 3409ea26761..3cb2a17b7ae 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt @@ -1,4 +1,5 @@ -[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("Akka.Cluster")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("Akka.Benchmarks")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("Akka.Cluster")] [assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("Akka.Cluster.TestKit")] [assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("Akka.Cluster.Tests")] [assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("Akka.Cluster.Tests.MultiNode")] diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveStreams.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveStreams.approved.txt index 1f3209c2649..f5535bc1fd2 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveStreams.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveStreams.approved.txt @@ -1,4 +1,5 @@ -[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("Akka.Streams.TestKit")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("Akka.Benchmarks")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("Akka.Streams.TestKit")] [assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("Akka.Streams.Tests")] [assembly: System.Runtime.InteropServices.ComVisibleAttribute(false)] [assembly: System.Runtime.InteropServices.GuidAttribute("123b83e9-21f8-49a8-888a-3b1212ff21dc")]