Skip to content

Commit 5edc518

Browse files
batzenCopilot
andauthored
Improving DynamicResource performance (second try) (#10532)
* Using alternative storage for dynamic resources * Fixing removal * Removing not required check Co-authored-by: Copilot <[email protected]> * Improving Contains * Addressing PR feedback * Adjusting code to false positive IDE warning... * Addressing PR feedback --------- Co-authored-by: Copilot <[email protected]>
1 parent 7f18269 commit 5edc518

File tree

4 files changed

+165
-248
lines changed

4 files changed

+165
-248
lines changed

src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Baml2006/Baml2006KeyRecord.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
namespace System.Windows.Baml2006
77
{
8-
[DebuggerDisplay("{DebuggerString}")]
8+
[DebuggerDisplay("{_data}")]
99
internal class KeyRecord
1010
{
1111
public KeyRecord(bool shared, bool sharedSet, int valuePosition, Type keyType) :
@@ -70,7 +70,7 @@ public StaticResource LastStaticResource
7070

7171
public string KeyString
7272
{
73-
get { return _data as String; }
73+
get { return _data as string; }
7474
}
7575

7676
public Type KeyType

src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ResourceDictionary.cs

Lines changed: 132 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,8 @@ private void ClearWithoutLock()
695695
// to the old resource before we clear it.
696696
ValidateDeferredResourceReferences(null);
697697

698+
_weakDeferredResourceReferencesMap?.Clear();
699+
698700
// remove inheritance context from all values that got it from
699701
// this dictionary
700702
RemoveInheritanceContextFromValues();
@@ -706,6 +708,61 @@ private void ClearWithoutLock()
706708
}
707709
}
708710

711+
// optimized contains method used in FetchResource
712+
private void Contains(object key, bool mustReturnDeferredResourceReference, out bool contains, out bool containsBamlObjectFactory)
713+
{
714+
contains = false;
715+
containsBamlObjectFactory = false;
716+
717+
bool result = _baseDictionary.Contains(key);
718+
719+
if (result)
720+
{
721+
KeyRecord keyRecord = _baseDictionary[key] as KeyRecord;
722+
if (keyRecord != null && _deferredLocationList.Contains(keyRecord))
723+
{
724+
contains = false;
725+
}
726+
else
727+
{
728+
contains = true;
729+
}
730+
731+
if (mustReturnDeferredResourceReference
732+
&& contains)
733+
{
734+
return;
735+
}
736+
737+
containsBamlObjectFactory = keyRecord is not null;
738+
739+
if (containsBamlObjectFactory)
740+
{
741+
return;
742+
}
743+
}
744+
745+
//Search for the value in the Merged Dictionaries
746+
if (_mergedDictionaries != null)
747+
{
748+
for (int i = MergedDictionaries.Count - 1; i > -1; i--)
749+
{
750+
// Note that MergedDictionaries collection can also contain null values
751+
ResourceDictionary mergedDictionary = MergedDictionaries[i];
752+
if (mergedDictionary != null)
753+
{
754+
mergedDictionary.Contains(key, mustReturnDeferredResourceReference, out contains, out containsBamlObjectFactory);
755+
756+
if (containsBamlObjectFactory
757+
|| (mustReturnDeferredResourceReference && contains))
758+
{
759+
return;
760+
}
761+
}
762+
}
763+
}
764+
}
765+
709766
/// <summary>
710767
/// Determines whether the IDictionary contains an element with the specified key.
711768
/// if the Key is not contained in this ResourceDictionary, it will check in the MergedDictionaries too
@@ -1718,8 +1775,10 @@ private object FetchResource(
17181775

17191776
if (allowDeferredResourceReference)
17201777
{
1721-
if (ContainsBamlObjectFactory(resourceKey) ||
1722-
(mustReturnDeferredResourceReference && Contains(resourceKey)))
1778+
Contains(resourceKey, mustReturnDeferredResourceReference, out bool contains, out bool containsBamlObjectFactory);
1779+
1780+
if (containsBamlObjectFactory
1781+
|| (mustReturnDeferredResourceReference && contains))
17231782
{
17241783
canCache = false;
17251784

@@ -1750,19 +1809,8 @@ private object FetchResource(
17501809
{
17511810
// Cache the deferredResourceReference so that it can be validated
17521811
// in case of a dictionary change prior to its inflation
1753-
_deferredResourceReferencesList ??= new DeferredResourceReferenceList();
1754-
1755-
if (_deferredResourceReferencesList.Get(resourceKey) is { } existingDeferredResourceReference
1756-
&& existingDeferredResourceReference.Dictionary == this)
1757-
{
1758-
deferredResourceReference = existingDeferredResourceReference;
1759-
}
1760-
else
1761-
{
1762-
deferredResourceReference = _ownerApps is not null ? new DeferredAppResourceReference(this, resourceKey) : new DeferredResourceReference(this, resourceKey);
1763-
1764-
_deferredResourceReferencesList.AddOrSet(deferredResourceReference);
1765-
}
1812+
deferredResourceReference = _ownerApps is not null ? new DeferredAppResourceReference(this, resourceKey) : new DeferredResourceReference(this, resourceKey);
1813+
GetOrCreateWeakReferenceList(resourceKey).Add(deferredResourceReference, true /*SkipFind*/);
17661814
}
17671815
}
17681816
else
@@ -1779,6 +1827,35 @@ private object FetchResource(
17791827
return GetValue(resourceKey, out canCache);
17801828
}
17811829

1830+
private WeakReferenceList GetOrCreateWeakReferenceList(object resourceKey)
1831+
{
1832+
this._weakDeferredResourceReferencesMap ??= new();
1833+
1834+
if (!this._weakDeferredResourceReferencesMap.TryGetValue(resourceKey, out var weakDeferredResourceReferences))
1835+
{
1836+
weakDeferredResourceReferences = new WeakReferenceList();
1837+
this._weakDeferredResourceReferencesMap[resourceKey] = weakDeferredResourceReferences;
1838+
}
1839+
1840+
return weakDeferredResourceReferences;
1841+
}
1842+
1843+
internal void RemoveDeferredResourceReference(DeferredResourceReference deferredResourceReference)
1844+
{
1845+
if (FrameworkAppContextSwitches.DisableDynamicResourceOptimization)
1846+
{
1847+
_weakDeferredResourceReferences?.Remove(deferredResourceReference);
1848+
}
1849+
else
1850+
{
1851+
//GetWeakReferenceList(deferredResourceReference.Key)?.Remove(deferredResourceReference);
1852+
if (this._weakDeferredResourceReferencesMap?.TryGetValue(deferredResourceReference.Key, out var weakDeferredResourceReferences) is true)
1853+
{
1854+
weakDeferredResourceReferences.Remove(deferredResourceReference);
1855+
}
1856+
}
1857+
}
1858+
17821859
/// <summary>
17831860
/// Validate the deferredResourceReference with the given key. Key could be null meaning
17841861
/// some catastrophic operation occurred so simply validate all DeferredResourceReferences
@@ -1804,34 +1881,45 @@ private void ValidateDeferredResourceReferences(object resourceKey)
18041881
}
18051882
else
18061883
{
1807-
if (_deferredResourceReferencesList is null)
1884+
if (_weakDeferredResourceReferencesMap is null)
18081885
{
18091886
return;
18101887
}
18111888

18121889
if (resourceKey is null)
18131890
{
1814-
foreach (DeferredResourceReference deferredResourceReference in _deferredResourceReferencesList)
1891+
foreach (var weakDeferredResourceReferences in _weakDeferredResourceReferencesMap.Values)
18151892
{
1816-
Inflate(deferredResourceReference);
1893+
foreach (var weakResourceReference in weakDeferredResourceReferences)
1894+
{
1895+
DeferredResourceReference deferredResourceReference = weakResourceReference as DeferredResourceReference;
1896+
1897+
Inflate(deferredResourceReference);
1898+
}
18171899
}
18181900
}
18191901
else
18201902
{
1821-
DeferredResourceReference deferredResourceReference = _deferredResourceReferencesList.Get(resourceKey);
1903+
if (_weakDeferredResourceReferencesMap.TryGetValue(resourceKey, out var weakDeferredResourceReferences))
1904+
{
1905+
foreach (var weakResourceReference in weakDeferredResourceReferences)
1906+
{
1907+
DeferredResourceReference deferredResourceReference = weakResourceReference as DeferredResourceReference;
18221908

1823-
Inflate(deferredResourceReference);
1909+
Inflate(deferredResourceReference);
1910+
}
1911+
}
18241912
}
1913+
}
18251914

1826-
return;
1915+
return;
18271916

1828-
void Inflate(DeferredResourceReference deferredResourceReference)
1829-
{
1830-
// This will inflate the deferred reference, causing it
1831-
// to be removed from the list. The list may also be
1832-
// purged of dead references.
1833-
deferredResourceReference?.GetValue(BaseValueSourceInternal.Unknown);
1834-
}
1917+
void Inflate(DeferredResourceReference deferredResourceReference)
1918+
{
1919+
// This will inflate the deferred reference, causing it
1920+
// to be removed from the list. The list may also be
1921+
// purged of dead references.
1922+
deferredResourceReference?.GetValue(BaseValueSourceInternal.Unknown);
18351923
}
18361924
}
18371925

@@ -2095,20 +2183,6 @@ internal WeakReferenceList ApplicationOwners
20952183

20962184
#endregion HelperMethods
20972185

2098-
#region Properties
2099-
2100-
internal WeakReferenceList WeakDeferredResourceReferences
2101-
{
2102-
get { return _weakDeferredResourceReferences; }
2103-
}
2104-
2105-
internal DeferredResourceReferenceList DeferredResourceReferencesList
2106-
{
2107-
get { return _deferredResourceReferencesList; }
2108-
}
2109-
2110-
#endregion Properties
2111-
21122186
#region Enumeration
21132187

21142188
/// <summary>
@@ -2519,11 +2593,11 @@ private void CopyDeferredContentFrom(ResourceDictionary loadedRD)
25192593
IsUnsafe = loadedRD.IsUnsafe;
25202594
}
25212595

2522-
private void MoveDeferredResourceReferencesFrom(ResourceDictionary loadedRD)
2596+
private void MoveDeferredResourceReferencesFrom(ResourceDictionary loadedRD)
25232597
{
25242598
if (FrameworkAppContextSwitches.DisableDynamicResourceOptimization)
25252599
{
2526-
// copy the list
2600+
// move the list
25272601
_weakDeferredResourceReferences = loadedRD._weakDeferredResourceReferences;
25282602

25292603
// redirect each entry toward its new owner
@@ -2537,11 +2611,20 @@ private void MoveDeferredResourceReferencesFrom(ResourceDictionary loadedRD)
25372611
}
25382612
else
25392613
{
2540-
// copy the list
2541-
_deferredResourceReferencesList = loadedRD._deferredResourceReferencesList;
2614+
// move the map and thus the lists
2615+
_weakDeferredResourceReferencesMap = loadedRD._weakDeferredResourceReferencesMap;
25422616

2543-
// redirect each entry toward its new owner
2544-
_deferredResourceReferencesList?.ChangeDictionary(this);
2617+
if (_weakDeferredResourceReferencesMap is not null)
2618+
{
2619+
// redirect each entry toward its new owner
2620+
foreach (var weakDeferredResourceReferences in _weakDeferredResourceReferencesMap.Values)
2621+
{
2622+
foreach (DeferredResourceReference drr in weakDeferredResourceReferences)
2623+
{
2624+
drr.Dictionary = this;
2625+
}
2626+
}
2627+
}
25452628
}
25462629
}
25472630

@@ -2609,7 +2692,7 @@ private enum FallbackState
26092692
private WeakReferenceList _ownerFCEs = null;
26102693
private WeakReferenceList _ownerApps = null;
26112694
private WeakReferenceList _weakDeferredResourceReferences = null;
2612-
private DeferredResourceReferenceList _deferredResourceReferencesList = null;
2695+
private Dictionary<object, WeakReferenceList> _weakDeferredResourceReferencesMap = null;
26132696
private ObservableCollection<ResourceDictionary> _mergedDictionaries = null;
26142697
private Uri _source = null;
26152698
private Uri _baseUri = null;

src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/StyleHelper.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -662,18 +662,19 @@ private static bool RequiresInstanceStorage(ref object value)
662662
Type valueType = deferredReference.GetValueType();
663663
if (valueType != null)
664664
{
665-
if (typeof(MarkupExtension).IsAssignableFrom(valueType))
665+
// Check for Freezable first, as that's way more common than MarkupExtension
666+
if (typeof(Freezable).IsAssignableFrom(valueType))
667+
{
668+
freezable = (Freezable)deferredReference.GetValue(BaseValueSourceInternal.Style);
669+
}
670+
else if (typeof(MarkupExtension).IsAssignableFrom(valueType))
666671
{
667672
value = deferredReference.GetValue(BaseValueSourceInternal.Style);
668673
if ((markupExtension = value as MarkupExtension) == null)
669674
{
670675
freezable = value as Freezable;
671676
}
672677
}
673-
else if (typeof(Freezable).IsAssignableFrom(valueType))
674-
{
675-
freezable = (Freezable)deferredReference.GetValue(BaseValueSourceInternal.Style);
676-
}
677678
}
678679

679680
}

0 commit comments

Comments
 (0)