From 0d5b0761f4aa9362c197f014cfa208a9b5d9478a Mon Sep 17 00:00:00 2001 From: Andrew El Date: Sat, 10 Apr 2021 12:26:47 -0400 Subject: [PATCH 1/6] Add StringBuilder pooling in NewtonsoftJsonSerializer --- .../SerializationBenchmarks/Program.cs | 82 +++++++++++- src/core/Akka/Akka.csproj | 1 + src/core/Akka/Configuration/Pigeon.conf | 25 +++- .../Serialization/NewtonSoftJsonSerializer.cs | 126 +++++++++++++++--- 4 files changed, 215 insertions(+), 19 deletions(-) diff --git a/src/benchmark/SerializationBenchmarks/Program.cs b/src/benchmark/SerializationBenchmarks/Program.cs index bf322edb785..193bf5bdc5a 100644 --- a/src/benchmark/SerializationBenchmarks/Program.cs +++ b/src/benchmark/SerializationBenchmarks/Program.cs @@ -6,12 +6,16 @@ //----------------------------------------------------------------------- using System; +using System.IO; +using System.Linq; using System.Runtime.CompilerServices; +using System.Threading.Tasks; using Akka.Actor; using Akka.Configuration; using Akka.Serialization; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; +using Newtonsoft.Json; namespace SerializationBenchmarks { @@ -19,7 +23,83 @@ class Program { static void Main(string[] args) { - BenchmarkRunner.Run(); + BenchmarkRunner.Run(); + } + } + + public class TestSer + { + public int Id { get; set; } + public string someStr { get; set; } + public string someStr2 { get; set; } + public string someStr3 { get; set; } + public Guid IDK { get; set; } + } + [MemoryDiagnoser] + public class JsonSerializerTests + { + public JsonSerializerTests() + { + _sys_noPool = ActorSystem.Create("bench-serialization-json-nopool",ConfigurationFactory.ParseString(@" +akka.actor {{ + +serialization-settings {{ + json {{ + use-pooled-string-builder = false + }} +}} + }}")); + _sys_pool = ActorSystem.Create("bench-serialization-json-pool"); + _noPoolSer = + _sys_noPool.Serialization.FindSerializerForType(typeof(object)); + _poolSer = + _sys_pool.Serialization.FindSerializerForType(typeof(object)); + } + private static TestSer testObj = new TestSer() + { + Id = 124, + someStr = + "412tgieoargj4a9349u2u-03jf3290rjf2390ja209fj1099u42n0f92qm93df3m-032jfq-102", + someStr2 = + "412tgieoargj4a9349u2u-03jf3290rjf2390ja209fj1099u42n0f92qm93df3m-032jfq-102", + someStr3 = + new string(Enumerable.Repeat('l',512).ToArray()), + IDK = Guid.Empty + }; + + private ActorSystem _sys_noPool; + private ActorSystem _sys_pool; + private Serializer _noPoolSer; + private Serializer _poolSer; + + [Benchmark] + public void Pooling() + { + for (int i = 0; i < 10000; i++) + { + _poolSer.ToBinary(testObj); + } + } + [Benchmark] + public void NoPooling() + { + for (int i = 0; i < 10000; i++) + { + _noPoolSer.ToBinary(testObj); + } + } + + [Benchmark] + public void Pooling_MultiTasks() + { + Task.WaitAll(Enumerable.Repeat(0, 10) + .Select((l) => Task.Run(Pooling)).ToArray()); + } + [Benchmark] + public void NoPooling_MultiTasks() + { + Task.WaitAll(Enumerable.Repeat(0, 10) + .Select((l) => Task.Run(NoPooling)).ToArray()); } } diff --git a/src/core/Akka/Akka.csproj b/src/core/Akka/Akka.csproj index d5af4e1eea7..27956f4f0e5 100644 --- a/src/core/Akka/Akka.csproj +++ b/src/core/Akka/Akka.csproj @@ -15,6 +15,7 @@ + diff --git a/src/core/Akka/Configuration/Pigeon.conf b/src/core/Akka/Configuration/Pigeon.conf index a2d17b96a50..77af1eb5217 100644 --- a/src/core/Akka/Configuration/Pigeon.conf +++ b/src/core/Akka/Configuration/Pigeon.conf @@ -522,9 +522,28 @@ akka { } # extra settings that can be custom to a serializer implementation - serialization-settings { - - } + serialization-settings { + json { + + # Used to set whether to use stringbuilders from a pool + # In memory constrained conditions (i.e. IOT) + # You may wish to turn this off + use-pooled-string-builder = true + + # The starting size of stringbuilders created in pool + # if use-pooled-string-builder is true. + # You may wish to adjust this number, + # For example if you are confident your messages are smaller or larger + pooled-string-builder-minsize = 2048b + + # The maximum retained size of a pooled stringbuilder + # if use-pooled-string-builder is true. + # You may wish to turn this number up if your messages are larger + # But do keep in mind that around 85K you'll wind up + # on the Large Object Heap (which may not be a bad thing...) + pooled-string-builder-maxsize = 32768b + } + } } # Used to set the behavior of the scheduler. diff --git a/src/core/Akka/Serialization/NewtonSoftJsonSerializer.cs b/src/core/Akka/Serialization/NewtonSoftJsonSerializer.cs index 274c873a4c5..edd247ed3cc 100644 --- a/src/core/Akka/Serialization/NewtonSoftJsonSerializer.cs +++ b/src/core/Akka/Serialization/NewtonSoftJsonSerializer.cs @@ -8,12 +8,14 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using System.Reflection; using System.Text; using Akka.Actor; using Akka.Configuration; using Akka.Util; +using Microsoft.Extensions.ObjectPool; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; @@ -32,7 +34,10 @@ public sealed class NewtonSoftJsonSerializerSettings public static readonly NewtonSoftJsonSerializerSettings Default = new NewtonSoftJsonSerializerSettings( encodeTypeNames: true, preserveObjectReferences: true, - converters: Enumerable.Empty()); + converters: Enumerable.Empty(), + usePooledStringBuilder:true, + stringBuilderMinSize:2048, + stringBuilderMaxSize:32768); /// /// Creates a new instance of the based on a provided . @@ -53,7 +58,11 @@ public static NewtonSoftJsonSerializerSettings Create(Config config) return new NewtonSoftJsonSerializerSettings( encodeTypeNames: config.GetBoolean("encode-type-names", true), preserveObjectReferences: config.GetBoolean("preserve-object-references", true), - converters: GetConverterTypes(config)); + converters: GetConverterTypes(config), + config.GetBoolean("use-pooled-string-builder",true), + config.GetInt("pooled-string-builder-minsize",2048), + config.GetInt("pooled-string-builder-maxsize", 32768) + ); } private static IEnumerable GetConverterTypes(Config config) @@ -89,6 +98,19 @@ private static IEnumerable GetConverterTypes(Config config) /// Converters must inherit from class and implement a default constructor. /// public IEnumerable Converters { get; } + + /// + /// The Starting size used for Pooled StringBuilders, if is -true- + /// + public int StringBuilderMinSize { get; } + /// + /// The Max Retained size for Pooled StringBuilders, if is -true- + /// + public int StringBuilderMaxSize { get; } + /// + /// If -true-, Stringbuilders are pooled and reused for serialization to lower memory pressure. + /// + public bool UsePooledStringBuilder { get; } /// /// Creates a new instance of the . @@ -96,7 +118,10 @@ private static IEnumerable GetConverterTypes(Config config) /// Determines if a special `$type` field should be emitted into serialized JSON. Must be true if corresponding serializer is used as default. /// Determines if object references should be tracked within serialized object graph. Must be true if corresponding serialize is used as default. /// A list of types implementing a to support custom types serialization. - public NewtonSoftJsonSerializerSettings(bool encodeTypeNames, bool preserveObjectReferences, IEnumerable converters) + /// Determines if string builders will be used from a pool to lower memory usage + /// Starting size used for pooled string builders if enabled + /// Max retained size used for pooled string builders if enabled + public NewtonSoftJsonSerializerSettings(bool encodeTypeNames, bool preserveObjectReferences, IEnumerable converters, bool usePooledStringBuilder, int stringBuilderMinSize, int stringBuilderMaxSize) { if (converters == null) throw new ArgumentNullException(nameof(converters), $"{nameof(NewtonSoftJsonSerializerSettings)} requires a sequence of converters."); @@ -104,6 +129,9 @@ public NewtonSoftJsonSerializerSettings(bool encodeTypeNames, bool preserveObjec EncodeTypeNames = encodeTypeNames; PreserveObjectReferences = preserveObjectReferences; Converters = converters; + UsePooledStringBuilder = usePooledStringBuilder; + StringBuilderMinSize = stringBuilderMinSize; + StringBuilderMaxSize = stringBuilderMaxSize; } } @@ -114,7 +142,8 @@ public NewtonSoftJsonSerializerSettings(bool encodeTypeNames, bool preserveObjec public class NewtonSoftJsonSerializer : Serializer { private readonly JsonSerializer _serializer; - + private readonly JsonSerializer _formattingNoneSerializer; + private readonly ObjectPool _sbPool; /// /// TBD /// @@ -138,12 +167,31 @@ public NewtonSoftJsonSerializer(ExtendedActorSystem system, Config config) : this(system, NewtonSoftJsonSerializerSettings.Create(config)) { } - - + public NewtonSoftJsonSerializer(ExtendedActorSystem system, NewtonSoftJsonSerializerSettings settings) : base(system) { - Settings = new JsonSerializerSettings + if (settings.UsePooledStringBuilder) + { + _sbPool = new DefaultObjectPool( + new StringBuilderPooledObjectPolicy() + { + InitialCapacity = settings.StringBuilderMinSize, + MaximumRetainedCapacity = + settings.StringBuilderMaxSize + }); + } + Settings = createInternalSettings(system, settings); + var settingsNoFormat = createInternalSettings(system, settings); + settingsNoFormat.Formatting = Formatting.None; + _serializer = JsonSerializer.Create(Settings); + _formattingNoneSerializer = JsonSerializer.Create(settingsNoFormat); + } + + private JsonSerializerSettings createInternalSettings( + ExtendedActorSystem system, NewtonSoftJsonSerializerSettings settings) + { + var newSettings = new JsonSerializerSettings { PreserveReferencesHandling = settings.PreserveObjectReferences ? PreserveReferencesHandling.Objects @@ -151,7 +199,8 @@ public NewtonSoftJsonSerializer(ExtendedActorSystem system, NewtonSoftJsonSerial NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore, MissingMemberHandling = MissingMemberHandling.Ignore, - ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor, + ConstructorHandling = + ConstructorHandling.AllowNonPublicDefaultConstructor, TypeNameHandling = settings.EncodeTypeNames ? TypeNameHandling.All : TypeNameHandling.None, @@ -159,8 +208,9 @@ public NewtonSoftJsonSerializer(ExtendedActorSystem system, NewtonSoftJsonSerial if (system != null) { - var settingsSetup = system.Settings.Setup.Get() - .GetOrElse(NewtonSoftJsonSerializerSetup.Create(s => {})); + var settingsSetup = system.Settings.Setup + .Get() + .GetOrElse(NewtonSoftJsonSerializerSetup.Create(s => { })); settingsSetup.ApplySettings(Settings); } @@ -174,15 +224,22 @@ public NewtonSoftJsonSerializer(ExtendedActorSystem system, NewtonSoftJsonSerial foreach (var converter in converters) { - Settings.Converters.Add(converter); + newSettings.Converters.Add(converter); } - Settings.ObjectCreationHandling = ObjectCreationHandling.Replace; //important: if reuse, the serializer will overwrite properties in default references, e.g. Props.DefaultDeploy or Props.noArgs - Settings.ContractResolver = new AkkaContractResolver(); - - _serializer = JsonSerializer.Create(Settings); + newSettings.ObjectCreationHandling = + ObjectCreationHandling + .Replace; //important: if reuse, the serializer will overwrite properties in default references, e.g. Props.DefaultDeploy or Props.noArgs + newSettings.ContractResolver = new AkkaContractResolver(); + if (settings.UsePooledStringBuilder) + { + + } + return newSettings; } + + private static JsonConverter CreateConverter(Type converterType, ExtendedActorSystem actorSystem) { var ctor = converterType.GetConstructors() @@ -228,12 +285,51 @@ protected override JsonProperty CreateProperty(MemberInfo member, MemberSerializ /// The object to serialize /// A byte array containing the serialized object public override byte[] ToBinary(object obj) + { + if (_sbPool != null) + { + return toBinary_PooledBuilder(obj); + } + else + { + return toBinary_NewBuilder(obj); + } + + } + + private byte[] toBinary_NewBuilder(object obj) { string data = JsonConvert.SerializeObject(obj, Formatting.None, Settings); byte[] bytes = Encoding.UTF8.GetBytes(data); return bytes; } + private byte[] toBinary_PooledBuilder(object obj) + { + //Don't try to opt with + //StringBuilder sb = _sbPool.Get() + //Or removing null check + //Both are necessary to avoid leaking on thread aborts etc + StringBuilder sb = null; + try + { + sb = _sbPool.Get(); + using (var tw = new StringWriter(sb)) + using (var jw = new JsonTextWriter(tw)) + { + _formattingNoneSerializer.Serialize(jw,obj); + return Encoding.UTF8.GetBytes(tw.ToString()); + } + } + finally + { + if (sb != null) + { + _sbPool.Return(sb); + } + } + } + /// /// Deserializes a byte array into an object of type . /// From 7c72de512aa48a90be347bce2cfe5c434550808f Mon Sep 17 00:00:00 2001 From: Andrew El Date: Sat, 10 Apr 2021 12:45:40 -0400 Subject: [PATCH 2/6] Fix CreateInternalSettings to properly wire everything up and be harder to goof up later by making static, fix hocon reading --- .../Serialization/NewtonSoftJsonSerializer.cs | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/core/Akka/Serialization/NewtonSoftJsonSerializer.cs b/src/core/Akka/Serialization/NewtonSoftJsonSerializer.cs index edd247ed3cc..f78a5a20aac 100644 --- a/src/core/Akka/Serialization/NewtonSoftJsonSerializer.cs +++ b/src/core/Akka/Serialization/NewtonSoftJsonSerializer.cs @@ -57,12 +57,17 @@ public static NewtonSoftJsonSerializerSettings Create(Config config) return new NewtonSoftJsonSerializerSettings( encodeTypeNames: config.GetBoolean("encode-type-names", true), - preserveObjectReferences: config.GetBoolean("preserve-object-references", true), + preserveObjectReferences: config.GetBoolean( + "preserve-object-references", true), converters: GetConverterTypes(config), - config.GetBoolean("use-pooled-string-builder",true), - config.GetInt("pooled-string-builder-minsize",2048), - config.GetInt("pooled-string-builder-maxsize", 32768) - ); + config.GetBoolean("use-pooled-string-builder", true), + (int)Math.Min( + config.GetByteSize("pooled-string-builder-minsize", 2048) ?? + 2048, int.MaxValue), + (int)Math.Min( + config.GetByteSize("pooled-string-builder-maxsize", + 32768) ?? 32768, int.MaxValue) + ); } private static IEnumerable GetConverterTypes(Config config) @@ -181,15 +186,15 @@ public NewtonSoftJsonSerializer(ExtendedActorSystem system, NewtonSoftJsonSerial settings.StringBuilderMaxSize }); } - Settings = createInternalSettings(system, settings); - var settingsNoFormat = createInternalSettings(system, settings); + Settings = CreateInternalSettings(system, settings,this); + var settingsNoFormat = CreateInternalSettings(system, settings,this); settingsNoFormat.Formatting = Formatting.None; _serializer = JsonSerializer.Create(Settings); _formattingNoneSerializer = JsonSerializer.Create(settingsNoFormat); } - private JsonSerializerSettings createInternalSettings( - ExtendedActorSystem system, NewtonSoftJsonSerializerSettings settings) + private static JsonSerializerSettings CreateInternalSettings( + ExtendedActorSystem system, NewtonSoftJsonSerializerSettings settings, NewtonSoftJsonSerializer surrogateParent) { var newSettings = new JsonSerializerSettings { @@ -212,14 +217,14 @@ private JsonSerializerSettings createInternalSettings( .Get() .GetOrElse(NewtonSoftJsonSerializerSetup.Create(s => { })); - settingsSetup.ApplySettings(Settings); + settingsSetup.ApplySettings(newSettings); } var converters = settings.Converters .Select(type => CreateConverter(type, system)) .ToList(); - converters.Add(new SurrogateConverter(this)); + converters.Add(new SurrogateConverter(surrogateParent)); converters.Add(new DiscriminatedUnionConverter()); foreach (var converter in converters) @@ -231,10 +236,6 @@ private JsonSerializerSettings createInternalSettings( ObjectCreationHandling .Replace; //important: if reuse, the serializer will overwrite properties in default references, e.g. Props.DefaultDeploy or Props.noArgs newSettings.ContractResolver = new AkkaContractResolver(); - if (settings.UsePooledStringBuilder) - { - - } return newSettings; } From 66c87f40fc7936e14d6fd5d3302207ed8a92c750 Mon Sep 17 00:00:00 2001 From: Andrew El Date: Sat, 10 Apr 2021 13:45:04 -0400 Subject: [PATCH 3/6] don't make two settings. --- .../Serialization/NewtonSoftJsonSerializer.cs | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/core/Akka/Serialization/NewtonSoftJsonSerializer.cs b/src/core/Akka/Serialization/NewtonSoftJsonSerializer.cs index f78a5a20aac..3315021e48d 100644 --- a/src/core/Akka/Serialization/NewtonSoftJsonSerializer.cs +++ b/src/core/Akka/Serialization/NewtonSoftJsonSerializer.cs @@ -186,17 +186,7 @@ public NewtonSoftJsonSerializer(ExtendedActorSystem system, NewtonSoftJsonSerial settings.StringBuilderMaxSize }); } - Settings = CreateInternalSettings(system, settings,this); - var settingsNoFormat = CreateInternalSettings(system, settings,this); - settingsNoFormat.Formatting = Formatting.None; - _serializer = JsonSerializer.Create(Settings); - _formattingNoneSerializer = JsonSerializer.Create(settingsNoFormat); - } - - private static JsonSerializerSettings CreateInternalSettings( - ExtendedActorSystem system, NewtonSoftJsonSerializerSettings settings, NewtonSoftJsonSerializer surrogateParent) - { - var newSettings = new JsonSerializerSettings + Settings = new JsonSerializerSettings { PreserveReferencesHandling = settings.PreserveObjectReferences ? PreserveReferencesHandling.Objects @@ -217,29 +207,32 @@ private static JsonSerializerSettings CreateInternalSettings( .Get() .GetOrElse(NewtonSoftJsonSerializerSetup.Create(s => { })); - settingsSetup.ApplySettings(newSettings); + settingsSetup.ApplySettings(Settings); } var converters = settings.Converters .Select(type => CreateConverter(type, system)) .ToList(); - converters.Add(new SurrogateConverter(surrogateParent)); + converters.Add(new SurrogateConverter(this)); converters.Add(new DiscriminatedUnionConverter()); foreach (var converter in converters) { - newSettings.Converters.Add(converter); + Settings.Converters.Add(converter); } - newSettings.ObjectCreationHandling = + Settings.ObjectCreationHandling = ObjectCreationHandling .Replace; //important: if reuse, the serializer will overwrite properties in default references, e.g. Props.DefaultDeploy or Props.noArgs - newSettings.ContractResolver = new AkkaContractResolver(); - return newSettings; + Settings.ContractResolver = new AkkaContractResolver(); + + _serializer = JsonSerializer.Create(Settings); + _formattingNoneSerializer = JsonSerializer.CreateDefault(Settings); + _formattingNoneSerializer.Formatting = Formatting.None; } - + private static JsonConverter CreateConverter(Type converterType, ExtendedActorSystem actorSystem) { From 379a51f65371e67b3fe40a7f75882680936d613e Mon Sep 17 00:00:00 2001 From: Andrew El Date: Sat, 10 Apr 2021 14:52:31 -0400 Subject: [PATCH 4/6] Fixes for thread safety, fix using scopes. --- .../Serialization/NewtonSoftJsonSerializer.cs | 35 ++++++++----------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/src/core/Akka/Serialization/NewtonSoftJsonSerializer.cs b/src/core/Akka/Serialization/NewtonSoftJsonSerializer.cs index 3315021e48d..56596a18d69 100644 --- a/src/core/Akka/Serialization/NewtonSoftJsonSerializer.cs +++ b/src/core/Akka/Serialization/NewtonSoftJsonSerializer.cs @@ -147,7 +147,7 @@ public NewtonSoftJsonSerializerSettings(bool encodeTypeNames, bool preserveObjec public class NewtonSoftJsonSerializer : Serializer { private readonly JsonSerializer _serializer; - private readonly JsonSerializer _formattingNoneSerializer; + private readonly ObjectPool _sbPool; /// /// TBD @@ -178,13 +178,8 @@ public NewtonSoftJsonSerializer(ExtendedActorSystem system, NewtonSoftJsonSerial { if (settings.UsePooledStringBuilder) { - _sbPool = new DefaultObjectPool( - new StringBuilderPooledObjectPolicy() - { - InitialCapacity = settings.StringBuilderMinSize, - MaximumRetainedCapacity = - settings.StringBuilderMaxSize - }); + _sbPool = new DefaultObjectPoolProvider() + .CreateStringBuilderPool(settings.StringBuilderMinSize,settings.StringBuilderMaxSize); } Settings = new JsonSerializerSettings { @@ -194,8 +189,7 @@ public NewtonSoftJsonSerializer(ExtendedActorSystem system, NewtonSoftJsonSerial NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore, MissingMemberHandling = MissingMemberHandling.Ignore, - ConstructorHandling = - ConstructorHandling.AllowNonPublicDefaultConstructor, + ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor, TypeNameHandling = settings.EncodeTypeNames ? TypeNameHandling.All : TypeNameHandling.None, @@ -203,9 +197,8 @@ public NewtonSoftJsonSerializer(ExtendedActorSystem system, NewtonSoftJsonSerial if (system != null) { - var settingsSetup = system.Settings.Setup - .Get() - .GetOrElse(NewtonSoftJsonSerializerSetup.Create(s => { })); + var settingsSetup = system.Settings.Setup.Get() + .GetOrElse(NewtonSoftJsonSerializerSetup.Create(s => {})); settingsSetup.ApplySettings(Settings); } @@ -222,14 +215,10 @@ public NewtonSoftJsonSerializer(ExtendedActorSystem system, NewtonSoftJsonSerial Settings.Converters.Add(converter); } - Settings.ObjectCreationHandling = - ObjectCreationHandling - .Replace; //important: if reuse, the serializer will overwrite properties in default references, e.g. Props.DefaultDeploy or Props.noArgs + Settings.ObjectCreationHandling = ObjectCreationHandling.Replace; //important: if reuse, the serializer will overwrite properties in default references, e.g. Props.DefaultDeploy or Props.noArgs Settings.ContractResolver = new AkkaContractResolver(); - + _serializer = JsonSerializer.Create(Settings); - _formattingNoneSerializer = JsonSerializer.CreateDefault(Settings); - _formattingNoneSerializer.Formatting = Formatting.None; } @@ -308,10 +297,14 @@ private byte[] toBinary_PooledBuilder(object obj) try { sb = _sbPool.Get(); + var ser = JsonSerializer.CreateDefault(Settings); using (var tw = new StringWriter(sb)) - using (var jw = new JsonTextWriter(tw)) { - _formattingNoneSerializer.Serialize(jw,obj); + using (var jw = new JsonTextWriter(tw)) + { + ser.Formatting = Formatting.None; + ser.Serialize(jw, obj); + } return Encoding.UTF8.GetBytes(tw.ToString()); } } From ea00658d1b048b2d172a6cc19d344ecb858b5316 Mon Sep 17 00:00:00 2001 From: Andrew El Date: Sat, 10 Apr 2021 15:44:10 -0400 Subject: [PATCH 5/6] api approvals, chars are not bytes so lets not confuse ourselves in HOCON --- .../SerializationBenchmarks/Program.cs | 6 +++--- .../CoreAPISpec.ApproveCore.approved.txt | 5 ++++- src/core/Akka/Configuration/Pigeon.conf | 7 ++++--- .../Serialization/NewtonSoftJsonSerializer.cs | 19 +++++++++---------- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/benchmark/SerializationBenchmarks/Program.cs b/src/benchmark/SerializationBenchmarks/Program.cs index 193bf5bdc5a..9e7704e44ec 100644 --- a/src/benchmark/SerializationBenchmarks/Program.cs +++ b/src/benchmark/SerializationBenchmarks/Program.cs @@ -71,11 +71,11 @@ public JsonSerializerTests() private ActorSystem _sys_pool; private Serializer _noPoolSer; private Serializer _poolSer; - + private const int _numIters = 10000; [Benchmark] public void Pooling() { - for (int i = 0; i < 10000; i++) + for (int i = 0; i < _numIters; i++) { _poolSer.ToBinary(testObj); } @@ -83,7 +83,7 @@ public void Pooling() [Benchmark] public void NoPooling() { - for (int i = 0; i < 10000; i++) + for (int i = 0; i < _numIters; i++) { _noPoolSer.ToBinary(testObj); } diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt index f308c7c1783..ac871670abe 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt @@ -4653,10 +4653,13 @@ namespace Akka.Serialization public sealed class NewtonSoftJsonSerializerSettings { public static readonly Akka.Serialization.NewtonSoftJsonSerializerSettings Default; - public NewtonSoftJsonSerializerSettings(bool encodeTypeNames, bool preserveObjectReferences, System.Collections.Generic.IEnumerable converters) { } + public NewtonSoftJsonSerializerSettings(bool encodeTypeNames, bool preserveObjectReferences, System.Collections.Generic.IEnumerable converters, bool usePooledStringBuilder, int stringBuilderMinSize, int stringBuilderMaxSize) { } public System.Collections.Generic.IEnumerable Converters { get; } public bool EncodeTypeNames { get; } public bool PreserveObjectReferences { get; } + public int StringBuilderMinSize { get; } + public int StringBuilderMaxSize { get; } + public bool UsePooledStringBuilder { get; } public static Akka.Serialization.NewtonSoftJsonSerializerSettings Create(Akka.Configuration.Config config) { } } public sealed class NewtonSoftJsonSerializerSetup : Akka.Actor.Setup.Setup diff --git a/src/core/Akka/Configuration/Pigeon.conf b/src/core/Akka/Configuration/Pigeon.conf index 77af1eb5217..0fdfbe39d84 100644 --- a/src/core/Akka/Configuration/Pigeon.conf +++ b/src/core/Akka/Configuration/Pigeon.conf @@ -534,14 +534,15 @@ akka { # if use-pooled-string-builder is true. # You may wish to adjust this number, # For example if you are confident your messages are smaller or larger - pooled-string-builder-minsize = 2048b + pooled-string-builder-minsize = 2048 # The maximum retained size of a pooled stringbuilder # if use-pooled-string-builder is true. # You may wish to turn this number up if your messages are larger - # But do keep in mind that around 85K you'll wind up + # But do keep in mind that strings in .NET are UTF-16, + # So after ~42k characters you might wind up # on the Large Object Heap (which may not be a bad thing...) - pooled-string-builder-maxsize = 32768b + pooled-string-builder-maxsize = 32768 } } } diff --git a/src/core/Akka/Serialization/NewtonSoftJsonSerializer.cs b/src/core/Akka/Serialization/NewtonSoftJsonSerializer.cs index 56596a18d69..fc1af0297fb 100644 --- a/src/core/Akka/Serialization/NewtonSoftJsonSerializer.cs +++ b/src/core/Akka/Serialization/NewtonSoftJsonSerializer.cs @@ -60,13 +60,11 @@ public static NewtonSoftJsonSerializerSettings Create(Config config) preserveObjectReferences: config.GetBoolean( "preserve-object-references", true), converters: GetConverterTypes(config), - config.GetBoolean("use-pooled-string-builder", true), - (int)Math.Min( - config.GetByteSize("pooled-string-builder-minsize", 2048) ?? - 2048, int.MaxValue), - (int)Math.Min( - config.GetByteSize("pooled-string-builder-maxsize", - 32768) ?? 32768, int.MaxValue) + usePooledStringBuilder: config.GetBoolean("use-pooled-string-builder", true), + stringBuilderMinSize:config.GetInt("pooled-string-builder-minsize", 2048), + stringBuilderMaxSize: + config.GetInt("pooled-string-builder-maxsize", + 32768) ); } @@ -297,12 +295,13 @@ private byte[] toBinary_PooledBuilder(object obj) try { sb = _sbPool.Get(); - var ser = JsonSerializer.CreateDefault(Settings); - using (var tw = new StringWriter(sb)) + + using (var tw = new StringWriter(sb, CultureInfo.InvariantCulture)) { + var ser = JsonSerializer.CreateDefault(Settings); + ser.Formatting = Formatting.None; using (var jw = new JsonTextWriter(tw)) { - ser.Formatting = Formatting.None; ser.Serialize(jw, obj); } return Encoding.UTF8.GetBytes(tw.ToString()); From 2727bfa5f9e940b5435f979af008f2c019064f36 Mon Sep 17 00:00:00 2001 From: Andrew El Date: Sat, 10 Apr 2021 16:21:12 -0400 Subject: [PATCH 6/6] fix api approval --- src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt index ac871670abe..634a5f6e5b0 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt @@ -4657,8 +4657,8 @@ namespace Akka.Serialization public System.Collections.Generic.IEnumerable Converters { get; } public bool EncodeTypeNames { get; } public bool PreserveObjectReferences { get; } - public int StringBuilderMinSize { get; } public int StringBuilderMaxSize { get; } + public int StringBuilderMinSize { get; } public bool UsePooledStringBuilder { get; } public static Akka.Serialization.NewtonSoftJsonSerializerSettings Create(Akka.Configuration.Config config) { } }