diff --git a/.gitignore b/.gitignore index aab78dc4dff..938bba0fd03 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,9 @@ bld/ [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* +# NBench results +[Pp]erf[Rr]esult*/ + #NUNIT *.VisualState.xml TestResult.xml diff --git a/build.cmd b/build.cmd index 2d3c363daa4..0dbcfcd97ac 100644 --- a/build.cmd +++ b/build.cmd @@ -28,6 +28,7 @@ src\.nuget\NuGet.exe install FAKE -ConfigFile src\.nuget\Nuget.Config -OutputDir src\.nuget\NuGet.exe install xunit.runner.console -ConfigFile src\.nuget\Nuget.Config -OutputDirectory src\packages\FAKE -ExcludeVersion -Version 2.0.0 src\.nuget\NuGet.exe install nunit.runners -ConfigFile src\.nuget\Nuget.Config -OutputDirectory src\packages\FAKE -ExcludeVersion -Version 2.6.4 +src\.nuget\NuGet.exe install NBench.Runner -OutputDirectory src\packages -ExcludeVersion if not exist src\packages\SourceLink.Fake\tools\SourceLink.fsx ( src\.nuget\nuget.exe install SourceLink.Fake -ConfigFile src\.nuget\Nuget.Config -OutputDirectory src\packages -ExcludeVersion diff --git a/build.fsx b/build.fsx index 4d794fbd4a7..29380190202 100644 --- a/build.fsx +++ b/build.fsx @@ -52,6 +52,7 @@ printfn "Assembly version: %s\nNuget version; %s\n" release.AssemblyVersion rele let binDir = "bin" let testOutput = FullName "TestResults" +let perfOutput = FullName "PerfResults" let nugetDir = binDir @@ "nuget" let workingDir = binDir @@ "build" @@ -276,6 +277,40 @@ Target "MultiNodeTests" <| fun _ -> multiNodeTestAssemblies |> Seq.iter (runMultiNodeSpec) +//-------------------------------------------------------------------------------- +// NBench targets +//-------------------------------------------------------------------------------- +Target "NBench" <| fun _ -> + let testSearchPath = + let assemblyFilter = getBuildParamOrDefault "spec-assembly" String.Empty + sprintf "src/**/bin/Release/*%s*.Tests.Performance.dll" assemblyFilter + + mkdir perfOutput + let nbenchTestPath = findToolInSubPath "NBench.Runner.exe" "src/packges/NBench.Runner*" + let nbenchTestAssemblies = !! testSearchPath + printfn "Using NBench.Runner: %s" nbenchTestPath + + let runNBench assembly = + let spec = getBuildParam "spec" + + let args = new StringBuilder() + |> append assembly + |> append (sprintf "output-directory=\"%s\"" perfOutput) + |> toText + + let result = ExecProcess(fun info -> + info.FileName <- nbenchTestPath + info.WorkingDirectory <- (Path.GetDirectoryName (FullName nbenchTestPath)) + info.Arguments <- args) (System.TimeSpan.FromMinutes 15.0) (* Reasonably long-running task. *) + if result <> 0 then failwithf "NBench.Runner failed. %s %s" nbenchTestPath args + + nbenchTestAssemblies |> Seq.iter (runNBench) + +//-------------------------------------------------------------------------------- +// Clean NBench output +Target "CleanPerf" <| fun _ -> + DeleteDir perfOutput + //-------------------------------------------------------------------------------- // Nuget targets @@ -561,6 +596,9 @@ Target "HelpMultiNodeTests" <| fun _ -> "CleanTests" ==> "RunTests" "CleanTests" ==> "MultiNodeTests" +// NBench dependencies +"CleanPerf" ==> "NBench" + // nuget dependencies "CleanNuget" ==> "CreateNuget" "CleanNuget" ==> "BuildRelease" ==> "Nuget" @@ -572,6 +610,7 @@ Target "All" DoNothing "BuildRelease" ==> "All" "RunTests" ==> "All" "MultiNodeTests" ==> "All" +"NBench" ==> "All" "Nuget" ==> "All" Target "AllTests" DoNothing //used for Mono builds, due to Mono 4.0 bug with FAKE / NuGet https://github.com/fsharp/fsharp/issues/427 diff --git a/build.sh b/build.sh index 7004e2907eb..87102bf20ef 100755 --- a/build.sh +++ b/build.sh @@ -32,6 +32,9 @@ mono $SCRIPT_PATH/src/.nuget/NuGet.exe install FAKE -OutputDirectory $SCRIPT_PAT mono $SCRIPT_PATH/src/.nuget/NuGet.exe install xunit.runners -OutputDirectory $SCRIPT_PATH/src/packages/FAKE -ExcludeVersion -Version 2.0.0 mono $SCRIPT_PATH/src/.nuget/NuGet.exe install nunit.runners -OutputDirectory $SCRIPT_PATH/src/packages/FAKE -ExcludeVersion -Version 2.6.4 +mono $SCRIPT_PATH/src/.nuget/NuGet.exe install NBench.Runner -OutputDirectory $SCRIPT_PATH/src/packages -ExcludeVersion + + if ! [ -e $SCRIPT_PATH/src/packages/SourceLink.Fake/tools/SourceLink.fsx ] ; then mono $SCRIPT_PATH/src/.nuget/NuGet.exe install SourceLink.Fake -OutputDirectory $SCRIPT_PATH/src/packages -ExcludeVersion diff --git a/src/Akka.sln b/src/Akka.sln index e4302d47d6b..fdc0f1f8001 100644 --- a/src/Akka.sln +++ b/src/Akka.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.24606.1 +VisualStudioVersion = 14.0.23107.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{69279534-1DBA-4115-BF8B-03F77FC8125E}" EndProject @@ -235,6 +235,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Serialization.WireTest EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Logger.log4net", "contrib\loggers\Akka.Logger.log4net\Akka.Logger.log4net.csproj", "{DAEC9503-CA75-433F-83DE-E28965D94D56}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Tests.Performance", "core\Akka.Tests.Performance\Akka.Tests.Performance.csproj", "{B17DEDCB-D2AA-472D-82A0-D242B711F488}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug Mono|Any CPU = Debug Mono|Any CPU @@ -864,6 +866,14 @@ Global {DAEC9503-CA75-433F-83DE-E28965D94D56}.Release Mono|Any CPU.Build.0 = Release|Any CPU {DAEC9503-CA75-433F-83DE-E28965D94D56}.Release|Any CPU.ActiveCfg = Release|Any CPU {DAEC9503-CA75-433F-83DE-E28965D94D56}.Release|Any CPU.Build.0 = Release|Any CPU + {B17DEDCB-D2AA-472D-82A0-D242B711F488}.Debug Mono|Any CPU.ActiveCfg = Debug|Any CPU + {B17DEDCB-D2AA-472D-82A0-D242B711F488}.Debug Mono|Any CPU.Build.0 = Debug|Any CPU + {B17DEDCB-D2AA-472D-82A0-D242B711F488}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B17DEDCB-D2AA-472D-82A0-D242B711F488}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B17DEDCB-D2AA-472D-82A0-D242B711F488}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU + {B17DEDCB-D2AA-472D-82A0-D242B711F488}.Release Mono|Any CPU.Build.0 = Release|Any CPU + {B17DEDCB-D2AA-472D-82A0-D242B711F488}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B17DEDCB-D2AA-472D-82A0-D242B711F488}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -968,5 +978,6 @@ Global {CAA97041-CFC0-4081-9BD2-8B139E62A611} = {0E55F1F8-E212-43D7-A0C0-ACEA9794B0D7} {402FA351-D6C6-40FD-8868-F07156035919} = {0E55F1F8-E212-43D7-A0C0-ACEA9794B0D7} {DAEC9503-CA75-433F-83DE-E28965D94D56} = {FFEC736B-EDA3-433C-8564-7C14676601A1} + {B17DEDCB-D2AA-472D-82A0-D242B711F488} = {01167D3C-49C4-4CDE-9787-C176D139ACDD} EndGlobalSection EndGlobal diff --git a/src/SharedAssemblyInfo.cs b/src/SharedAssemblyInfo.cs index fcb5f369368..41fdd7e5345 100644 --- a/src/SharedAssemblyInfo.cs +++ b/src/SharedAssemblyInfo.cs @@ -4,5 +4,5 @@ [assembly: AssemblyCompanyAttribute("Akka.NET Team")] [assembly: AssemblyCopyrightAttribute("Copyright © 2013-2015 Akka.NET Team")] [assembly: AssemblyTrademarkAttribute("")] -[assembly: AssemblyVersionAttribute("1.0.5.0")] -[assembly: AssemblyFileVersionAttribute("1.0.5.0")] +[assembly: AssemblyVersionAttribute("1.0.6.0")] +[assembly: AssemblyFileVersionAttribute("1.0.6.0")] diff --git a/src/core/Akka.FSharp/Properties/AssemblyInfo.fs b/src/core/Akka.FSharp/Properties/AssemblyInfo.fs index 45ed219319b..ede3203756a 100644 --- a/src/core/Akka.FSharp/Properties/AssemblyInfo.fs +++ b/src/core/Akka.FSharp/Properties/AssemblyInfo.fs @@ -10,9 +10,9 @@ open System.Runtime.InteropServices [] [] [] -[] -[] +[] +[] do () module internal AssemblyVersionInformation = - let [] Version = "1.0.5.0" + let [] Version = "1.0.6.0" diff --git a/src/core/Akka.Remote/Akka.Remote.csproj b/src/core/Akka.Remote/Akka.Remote.csproj index 7a2d6e37faf..7d2a93758b2 100644 --- a/src/core/Akka.Remote/Akka.Remote.csproj +++ b/src/core/Akka.Remote/Akka.Remote.csproj @@ -51,9 +51,6 @@ false - - True - True diff --git a/src/core/Akka.Tests.Performance/Actor/ActorMemoryFootprintSpec.cs b/src/core/Akka.Tests.Performance/Actor/ActorMemoryFootprintSpec.cs new file mode 100644 index 00000000000..70245931d71 --- /dev/null +++ b/src/core/Akka.Tests.Performance/Actor/ActorMemoryFootprintSpec.cs @@ -0,0 +1,82 @@ +using System; +using Akka.Actor; +using Akka.Util.Internal; +using NBench; + +namespace Akka.Tests.Performance.Actor +{ + /// + /// Tests the default memory footprint of an Akka.NET actor + /// + public class ActorMemoryFootprintSpec + { + class MemoryUntypedActor : UntypedActor { + protected override void OnReceive(object message) + { + if (message is string) return; + if (message is int) return; + if (message is bool) return; + Unhandled(message); + } + } + + class MemoryReceiveActor : ReceiveActor + { + public MemoryReceiveActor() + { + Receive(s => { }); + Receive(i => { }); + Receive(b => { }); + } + } + + private static readonly AtomicCounter Counter = new AtomicCounter(0); + private ActorSystem _system; + private Counter _createActorThroughput; + + private const string CreateThroughputCounter = "ActorCreateThroughput"; + private const int ActorCreateNumber = 10000; + + private static readonly Props UntypedActorProps = Props.Create(() => new MemoryUntypedActor()); + private static readonly Props ReceiveActorProps = Props.Create(() => new MemoryReceiveActor()); + + [PerfSetup] + public void Setup(BenchmarkContext context) + { + _system = ActorSystem.Create("ActorMemoryFootprintSpec" + Counter.GetAndIncrement()); + _createActorThroughput = context.GetCounter(CreateThroughputCounter); + } + + [PerfBenchmark(Description = "Measures the amount of memory used by 10,000 UntypedActors", RunMode = RunMode.Iterations, NumberOfIterations = 13, TestMode = TestMode.Measurement)] + [MemoryMeasurement(MemoryMetric.TotalBytesAllocated)] + [CounterMeasurement(CreateThroughputCounter)] + public void UntypedActorMemoryFootprint(BenchmarkContext context) + { + for (var i = 0; i < ActorCreateNumber; i++) + { + _system.ActorOf(UntypedActorProps); + _createActorThroughput.Increment(); + } + } + + [PerfBenchmark(Description = "Measures the amount of memory used by 10,000 ReceiveActors", RunMode = RunMode.Iterations, NumberOfIterations = 13, TestMode = TestMode.Measurement)] + [MemoryMeasurement(MemoryMetric.TotalBytesAllocated)] + [CounterMeasurement(CreateThroughputCounter)] + public void ReceiveActorMemoryFootprint(BenchmarkContext context) + { + for (var i = 0; i < ActorCreateNumber; i++) + { + _system.ActorOf(ReceiveActorProps); + _createActorThroughput.Increment(); + } + } + + [PerfCleanup] + public void Teardown(BenchmarkContext context) + { + _system.Shutdown(); + _system.AwaitTermination(TimeSpan.FromSeconds(2.0d)); + _system = null; + } + } +} diff --git a/src/core/Akka.Tests.Performance/Akka.Tests.Performance.csproj b/src/core/Akka.Tests.Performance/Akka.Tests.Performance.csproj new file mode 100644 index 00000000000..8e43abc675d --- /dev/null +++ b/src/core/Akka.Tests.Performance/Akka.Tests.Performance.csproj @@ -0,0 +1,67 @@ + + + + + Debug + AnyCPU + {B17DEDCB-D2AA-472D-82A0-D242B711F488} + Library + Properties + Akka.Tests.Performance + Akka.Tests.Performance + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\..\packages\NBench.0.1.3\lib\net45\NBench.dll + True + + + + + + + + + + + + + {0d3cbad0-bbdb-43e5-afc4-ed1d3ecdc224} + Akka.TestKit + + + {5deddf90-37f0-48d3-a0b0-a5cbd8a7e377} + Akka + + + + + + + + \ No newline at end of file diff --git a/src/core/Akka.Tests.Performance/Dispatch/ConcurrentQueueMailboxThroughputSpec.cs b/src/core/Akka.Tests.Performance/Dispatch/ConcurrentQueueMailboxThroughputSpec.cs new file mode 100644 index 00000000000..de7f7d898b6 --- /dev/null +++ b/src/core/Akka.Tests.Performance/Dispatch/ConcurrentQueueMailboxThroughputSpec.cs @@ -0,0 +1,12 @@ +using Akka.Dispatch; + +namespace Akka.Tests.Performance.Dispatch +{ + public class ConcurrentQueueMailboxThroughputSpec : MailboxThroughputSpecBase + { + protected override Mailbox CreateMailbox() + { + return new ConcurrentQueueMailbox(); + } + } +} \ No newline at end of file diff --git a/src/core/Akka.Tests.Performance/Dispatch/MailboxMemoryFootprintSpec.cs b/src/core/Akka.Tests.Performance/Dispatch/MailboxMemoryFootprintSpec.cs new file mode 100644 index 00000000000..e2e648957ba --- /dev/null +++ b/src/core/Akka.Tests.Performance/Dispatch/MailboxMemoryFootprintSpec.cs @@ -0,0 +1,66 @@ +using System.Collections.Generic; +using Akka.Dispatch; +using NBench; + +namespace Akka.Tests.Performance.Dispatch +{ + public class MailboxMemoryFootprintSpec + { + private Counter _createMailboxThroughput; + + private const string CreateThroughputCounter = "MailboxCreateThroughput"; + private const int MailboxCreateNumber = 10000; + + private List _mailboxes; + + [PerfSetup] + public void Setup(BenchmarkContext context) + { + _createMailboxThroughput = context.GetCounter(CreateThroughputCounter); + _mailboxes = new List(MailboxCreateNumber); + } + + [PerfBenchmark(Description = "Measures the amount of memory used by 10,000 ConcurrentQueueMailboxes", RunMode = RunMode.Iterations, NumberOfIterations = 13, TestMode = TestMode.Measurement)] + [MemoryMeasurement(MemoryMetric.TotalBytesAllocated)] + [CounterMeasurement(CreateThroughputCounter)] + public void ConcurrentQueueMailbox() + { + for (var i = 0; i < MailboxCreateNumber; i++) + { + _mailboxes.Add(new ConcurrentQueueMailbox()); + _createMailboxThroughput.Increment(); + } + } + + [PerfBenchmark(Description = "Measures the amount of memory used by 10,000 UnboundedDequeBasedMailboxes", RunMode = RunMode.Iterations, NumberOfIterations = 13, TestMode = TestMode.Measurement)] + [MemoryMeasurement(MemoryMetric.TotalBytesAllocated)] + [CounterMeasurement(CreateThroughputCounter)] + public void UnboundedDequeBasedMailbox() + { + for (var i = 0; i < MailboxCreateNumber; i++) + { + _mailboxes.Add(new UnboundedDequeBasedMailbox()); + _createMailboxThroughput.Increment(); + } + } + + [PerfBenchmark(Description = "Measures the amount of memory used by 10,000 BoundedDequeBasedMailboxes", RunMode = RunMode.Iterations, NumberOfIterations = 13, TestMode = TestMode.Measurement)] + [MemoryMeasurement(MemoryMetric.TotalBytesAllocated)] + [CounterMeasurement(CreateThroughputCounter)] + public void BoundedDequeBasedMailbox() + { + for (var i = 0; i < MailboxCreateNumber; i++) + { + _mailboxes.Add(new BoundedDequeBasedMailbox()); + _createMailboxThroughput.Increment(); + } + } + + [PerfCleanup] + public void Teardown() + { + _mailboxes.Clear(); + _mailboxes = null; + } + } +} diff --git a/src/core/Akka.Tests.Performance/Dispatch/MailboxThroughputSpecBase.cs b/src/core/Akka.Tests.Performance/Dispatch/MailboxThroughputSpecBase.cs new file mode 100644 index 00000000000..f86c8dec72f --- /dev/null +++ b/src/core/Akka.Tests.Performance/Dispatch/MailboxThroughputSpecBase.cs @@ -0,0 +1,68 @@ +using Akka.Actor; +using Akka.Actor.Internal; +using Akka.Configuration; +using Akka.Dispatch; +using Akka.TestKit; +using Akka.Util.Internal; +using NBench; + +namespace Akka.Tests.Performance.Dispatch +{ + /// + /// Base class used to test the performance of different implementations + /// + public abstract class MailboxThroughputSpecBase + { + private const string MailboxCounterName = "MessageReceived"; + private static readonly Envelope TestEnvelope = new Envelope {Message = "", Sender = ActorRefs.NoSender}; + private ActorCell _actorCell; + private MessageDispatcher _dispatcher; + private Mailbox _mailbox; + private Counter _mailboxThroughput; + private IActorRef _receiver; + protected abstract Mailbox CreateMailbox(); + + private static readonly AtomicCounter Counter = new AtomicCounter(0); + private ActorSystem _system; + + class BenchmarkActor : UntypedActor + { + private Counter _counter; + + public BenchmarkActor(Counter counter) + { + _counter = counter; + } + + protected override void OnReceive(object message) + { + _counter.Increment(); + } + } + + [PerfSetup] + public void Setup(BenchmarkContext context) + { + _mailboxThroughput = context.GetCounter(MailboxCounterName); + _mailbox = CreateMailbox(); + _system = ActorSystem.Create("MailboxThroughputSpecBase" + Counter.GetAndIncrement()); + _receiver = _system.ActorOf(Props.Create(() => new BenchmarkActor(_mailboxThroughput))); + _actorCell = _receiver.AsInstanceOf().Underlying.AsInstanceOf(); + _dispatcher = _actorCell.Dispatcher; + _mailbox.Setup(_actorCell.Dispatcher); + _mailbox.SetActor(_actorCell); + _mailbox.Start(); + } + + [PerfBenchmark( + Description = + "Measures the raw message throughput of a mailbox implementation with no additional configuration options", + RunMode = RunMode.Throughput, NumberOfIterations = 13, TestMode = TestMode.Measurement, + RunTimeMilliseconds = 1000)] + [CounterMeasurement(MailboxCounterName)] + public void Benchmark(BenchmarkContext context) + { + _receiver.Tell(string.Empty); + } + } +} \ No newline at end of file diff --git a/src/core/Akka.Tests.Performance/Properties/AssemblyInfo.cs b/src/core/Akka.Tests.Performance/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..c807b338893 --- /dev/null +++ b/src/core/Akka.Tests.Performance/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Akka.Tests.Performance")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Akka.Tests.Performance")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 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 +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("b17dedcb-d2aa-472d-82a0-d242b711f488")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/core/Akka.Tests.Performance/packages.config b/src/core/Akka.Tests.Performance/packages.config new file mode 100644 index 00000000000..a9b01dcf1a4 --- /dev/null +++ b/src/core/Akka.Tests.Performance/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/core/Akka/Properties/AssemblyInfo.cs b/src/core/Akka/Properties/AssemblyInfo.cs index e0550a65f0a..125fc107461 100644 --- a/src/core/Akka/Properties/AssemblyInfo.cs +++ b/src/core/Akka/Properties/AssemblyInfo.cs @@ -28,6 +28,7 @@ [assembly: Guid("1a5cab08-b032-49ca-8db3-9428c5a9db14")] [assembly: InternalsVisibleTo("Akka.Tests")] +[assembly: InternalsVisibleTo("Akka.Tests.Performance")] [assembly: InternalsVisibleTo("Akka.TestKit")] [assembly: InternalsVisibleTo("Akka.TestKit.Tests")] [assembly: InternalsVisibleTo("Akka.Remote")]