From 028da4545ebe23df44ed03f2cbc7df21d41775c3 Mon Sep 17 00:00:00 2001 From: Henrik Date: Wed, 26 Mar 2025 16:01:53 +0100 Subject: [PATCH] Reduce CPU time when initiating RepositoryCacheKeys (#18267) * Avoid an unneeded lookups in the Keys dictionary when initiating key cache * Add further comments and unit tests around updated code. --------- Co-authored-by: Andy Butland --- .../Repositories/RepositoryCacheKeys.cs | 25 ++++++++++++++++-- .../Repositories/RepositoryCacheKeysTests.cs | 26 +++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/Persistence/Repositories/RepositoryCacheKeysTests.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryCacheKeys.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryCacheKeys.cs index a6b6c16aa5..aca68e9762 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RepositoryCacheKeys.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryCacheKeys.cs @@ -1,3 +1,5 @@ +using System.Runtime.InteropServices; + namespace Umbraco.Cms.Core.Persistence.Repositories; /// @@ -5,15 +7,34 @@ namespace Umbraco.Cms.Core.Persistence.Repositories; /// public static class RepositoryCacheKeys { - // used to cache keys so we don't keep allocating strings + /// + /// A cache for the keys we don't keep allocating strings. + /// private static readonly Dictionary Keys = new(); + /// + /// Gets the repository cache key for the provided type. + /// public static string GetKey() { Type type = typeof(T); - return Keys.TryGetValue(type, out var key) ? key : Keys[type] = "uRepo_" + type.Name + "_"; + + // The following code is a micro-optimization to avoid an unnecessary lookup in the Keys dictionary, when writing the newly created key. + // Previously, the code was: + // return Keys.TryGetValue(type, out var key) + // ? key + // : Keys[type] = "uRepo_" + type.Name + "_"; + + // Look up the existing value or get a reference to the newly created default value. + ref string? key = ref CollectionsMarshal.GetValueRefOrAddDefault(Keys, type, out _); + + // As we have the reference, we can just assign it if null, without the expensive write back to the dictionary. + return key ??= "uRepo_" + type.Name + "_"; } + /// + /// Gets the repository cache key for the provided type and Id. + /// public static string GetKey(TId? id) { if (EqualityComparer.Default.Equals(id, default)) diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Persistence/Repositories/RepositoryCacheKeysTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Persistence/Repositories/RepositoryCacheKeysTests.cs new file mode 100644 index 0000000000..fef83541b9 --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Persistence/Repositories/RepositoryCacheKeysTests.cs @@ -0,0 +1,26 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using NUnit.Framework; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Persistence.Repositories; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors; + +[TestFixture] +public class RepositoryCacheKeysTests +{ + [Test] + public void GetKey_Returns_Expected_Key_For_Type() + { + var key = RepositoryCacheKeys.GetKey(); + Assert.AreEqual("uRepo_IContent_", key); + } + + [Test] + public void GetKey_Returns_Expected_Key_For_Type_And_Id() + { + var key = RepositoryCacheKeys.GetKey(1000); + Assert.AreEqual("uRepo_IContent_1000", key); + } +}