Speed up boot times and Improve Json (De)Serialization performance and reduce memory usage by reusing JsonSerializerSettings (#9670)

This commit is contained in:
Chad
2021-02-21 23:00:00 +13:00
committed by GitHub
parent f27a3be4e6
commit 75ee3b9622
9 changed files with 112 additions and 26 deletions

View File

@@ -25,6 +25,12 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType)
=> PropertyCacheLevel.Element;
private static readonly JsonSerializerSettings ImageCropperValueJsonSerializerSettings = new JsonSerializerSettings
{
Culture = CultureInfo.InvariantCulture,
FloatParseHandling = FloatParseHandling.Decimal
};
/// <inheritdoc />
public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview)
{
@@ -34,11 +40,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
ImageCropperValue value;
try
{
value = JsonConvert.DeserializeObject<ImageCropperValue>(sourceString, new JsonSerializerSettings
{
Culture = CultureInfo.InvariantCulture,
FloatParseHandling = FloatParseHandling.Decimal
});
value = JsonConvert.DeserializeObject<ImageCropperValue>(sourceString, ImageCropperValueJsonSerializerSettings);
}
catch (Exception ex)
{

View File

@@ -19,6 +19,7 @@ namespace Umbraco.Core.Serialization
internal class NoTypeConverterJsonConverter<T> : JsonConverter
{
static readonly IContractResolver resolver = new NoTypeConverterContractResolver();
private static readonly JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings { ContractResolver = resolver };
private class NoTypeConverterContractResolver : DefaultContractResolver
{
@@ -41,12 +42,12 @@ namespace Umbraco.Core.Serialization
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return JsonSerializer.CreateDefault(new JsonSerializerSettings { ContractResolver = resolver }).Deserialize(reader, objectType);
return JsonSerializer.CreateDefault(JsonSerializerSettings).Deserialize(reader, objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JsonSerializer.CreateDefault(new JsonSerializerSettings { ContractResolver = resolver }).Serialize(writer, value);
JsonSerializer.CreateDefault(JsonSerializerSettings).Serialize(writer, value);
}
}
}

View File

@@ -0,0 +1,69 @@
using BenchmarkDotNet.Attributes;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Tests.Benchmarks.Config;
namespace Umbraco.Tests.Benchmarks
{
[QuickRunConfig]
[MemoryDiagnoser]
public class JsonSerializerSettingsBenchmarks
{
[Benchmark]
public void SerializerSettingsInstantiation()
{
int instances = 1000;
for (int i = 0; i < instances; i++)
{
new JsonSerializerSettings();
}
}
[Benchmark(Baseline =true)]
public void SerializerSettingsSingleInstantiation()
{
new JsonSerializerSettings();
}
// // * Summary *
// BenchmarkDotNet=v0.11.3, OS=Windows 10.0.18362
//Intel Core i5-8265U CPU 1.60GHz(Kaby Lake R), 1 CPU, 8 logical and 4 physical cores
// [Host] : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.8.4250.0
// Job-JIATTD : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.8.4250.0
//IterationCount=3 IterationTime=100.0000 ms LaunchCount = 1
//WarmupCount=3
// Method | Mean | Error | StdDev | Ratio | RatioSD | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op |
//-------------------------------------- |-------------:|-------------:|------------:|-------:|--------:|------------:|------------:|------------:|--------------------:|
// SerializerSettingsInstantiation | 29,120.48 ns | 5,532.424 ns | 303.2508 ns | 997.84 | 23.66 | 73.8122 | - | - | 232346 B |
// SerializerSettingsSingleInstantiation | 29.19 ns | 8.089 ns | 0.4434 ns | 1.00 | 0.00 | 0.0738 | - | - | 232 B |
//// * Warnings *
//MinIterationTime
// JsonSerializerSettingsBenchmarks.SerializerSettingsSingleInstantiation: IterationCount= 3, IterationTime= 100.0000 ms, LaunchCount= 1, WarmupCount= 3->MinIterationTime = 96.2493 ms which is very small. It's recommended to increase it.
//// * Legends *
// Mean : Arithmetic mean of all measurements
// Error : Half of 99.9% confidence interval
// StdDev : Standard deviation of all measurements
// Ratio : Mean of the ratio distribution ([Current]/[Baseline])
// RatioSD : Standard deviation of the ratio distribution([Current]/[Baseline])
// Gen 0/1k Op : GC Generation 0 collects per 1k Operations
// Gen 1/1k Op : GC Generation 1 collects per 1k Operations
// Gen 2/1k Op : GC Generation 2 collects per 1k Operations
// Allocated Memory/Op : Allocated memory per single operation(managed only, inclusive, 1KB = 1024B)
// 1 ns : 1 Nanosecond(0.000000001 sec)
//// * Diagnostic Output - MemoryDiagnoser *
// // ***** BenchmarkRunner: End *****
// Run time: 00:00:04 (4.88 sec), executed benchmarks: 2
}
}

View File

@@ -53,6 +53,7 @@
<Compile Include="CtorInvokeBenchmarks.cs" />
<Compile Include="HexStringBenchmarks.cs" />
<Compile Include="EnumeratorBenchmarks.cs" />
<Compile Include="JsonSerializerSettingsBenchmarks.cs" />
<Compile Include="LinqCastBenchmarks.cs" />
<Compile Include="ModelToSqlExpressionHelperBenchmarks.cs" />
<Compile Include="Program.cs" />

View File

@@ -226,7 +226,7 @@ namespace Umbraco.Web.Editors
.ToDictionary(pv => pv.Key, pv =>
pv.ToDictionary(pve => pve.valueAlias, pve => pve.value));
return new JsonNetResult { Data = nestedDictionary, Formatting = Formatting.None };
return new JsonNetResult(JsonNetResult.DefaultJsonSerializerSettings) { Data = nestedDictionary, Formatting = Formatting.None };
}
/// <summary>
@@ -273,7 +273,7 @@ namespace Umbraco.Web.Editors
GetAssetList,
new TimeSpan(0, 2, 0));
return new JsonNetResult { Data = result, Formatting = Formatting.None };
return new JsonNetResult(JsonNetResult.DefaultJsonSerializerSettings) { Data = result, Formatting = Formatting.None };
}
[UmbracoAuthorize(Order = 0)]
@@ -281,7 +281,7 @@ namespace Umbraco.Web.Editors
public JsonNetResult GetGridConfig()
{
var gridConfig = Current.Configs.Grids();
return new JsonNetResult { Data = gridConfig.EditorsConfig.Editors, Formatting = Formatting.None };
return new JsonNetResult(JsonNetResult.DefaultJsonSerializerSettings) { Data = gridConfig.EditorsConfig.Editors, Formatting = Formatting.None };
}

View File

@@ -253,19 +253,20 @@ namespace Umbraco.Web
ImageCropRatioMode? ratioMode = null,
bool upScale = true) => ImageCropperTemplateCoreExtensions.GetCropUrl(imageUrl, Current.ImageUrlGenerator, cropDataSet, width, height, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions, ratioMode, upScale);
private static readonly JsonSerializerSettings ImageCropperValueJsonSerializerSettings = new JsonSerializerSettings
{
Culture = CultureInfo.InvariantCulture,
FloatParseHandling = FloatParseHandling.Decimal
};
internal static ImageCropperValue DeserializeImageCropperValue(this string json)
{
var imageCrops = new ImageCropperValue();
ImageCropperValue imageCrops = null;
if (json.DetectIsJson())
{
try
{
imageCrops = JsonConvert.DeserializeObject<ImageCropperValue>(json, new JsonSerializerSettings
{
Culture = CultureInfo.InvariantCulture,
FloatParseHandling = FloatParseHandling.Decimal
});
imageCrops = JsonConvert.DeserializeObject<ImageCropperValue>(json, ImageCropperValueJsonSerializerSettings);
}
catch (Exception ex)
{
@@ -273,6 +274,7 @@ namespace Umbraco.Web
}
}
imageCrops = imageCrops ?? new ImageCropperValue();
return imageCrops;
}
}

View File

@@ -22,10 +22,19 @@ namespace Umbraco.Web.Mvc
public JsonSerializerSettings SerializerSettings { get; set; }
public Formatting Formatting { get; set; }
/// <summary>
/// Default, unchanged JsonSerializerSettings
/// </summary>
public static readonly JsonSerializerSettings DefaultJsonSerializerSettings = new JsonSerializerSettings();
public JsonNetResult()
{
SerializerSettings = new JsonSerializerSettings();
}
public JsonNetResult(JsonSerializerSettings jsonSerializerSettings)
{
SerializerSettings = jsonSerializerSettings;
}
public override void ExecuteResult(ControllerContext context)
{

View File

@@ -121,6 +121,10 @@ namespace Umbraco.Web.PropertyEditors
return base.ToEditor(property, dataTypeService, culture, segment);
}
private static readonly JsonSerializerSettings LinkDisplayJsonSerializerSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
};
public override object FromEditor(ContentPropertyData editorValue, object currentValue)
{
@@ -142,11 +146,8 @@ namespace Umbraco.Web.PropertyEditors
Target = link.Target,
Udi = link.Udi,
Url = link.Udi == null ? link.Url : null, // only save the URL for external links
},
new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
});
}, LinkDisplayJsonSerializerSettings
);
}
catch (Exception ex)
{

View File

@@ -303,17 +303,18 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
return s;
}
private static readonly JsonSerializerSettings NestedContentDataJsonSerializerSettings = new JsonSerializerSettings
{
Converters = new List<JsonConverter> { new ForceInt32Converter() }
};
private static ContentNestedData DeserializeNestedData(string data)
{
// by default JsonConvert will deserialize our numeric values as Int64
// which is bad, because they were Int32 in the database - take care
var settings = new JsonSerializerSettings
{
Converters = new List<JsonConverter> { new ForceInt32Converter() }
};
return JsonConvert.DeserializeObject<ContentNestedData>(data, settings);
return JsonConvert.DeserializeObject<ContentNestedData>(data, NestedContentDataJsonSerializerSettings);
}
}
}