Skip to content

Commit 240ec6a

Browse files
AlexUstinovSergeiPavlov
authored andcommitted
Optimize WeakCache
1 parent 92c0394 commit 240ec6a

File tree

12 files changed

+86
-112
lines changed

12 files changed

+86
-112
lines changed

Orm/Xtensive.Orm/Caching/WeakCache.cs

Lines changed: 52 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Collections;
99
using System.Collections.Generic;
1010
using System.Diagnostics;
11+
using System.Linq;
1112
using System.Runtime.InteropServices;
1213
using System.Security;
1314
using Xtensive.Core;
@@ -34,7 +35,6 @@ public class WeakCache<TKey, TItem> :
3435
/// </summary>
3536
protected const int NoGcCount = 1024;
3637

37-
private const int GcOperationCost = 2;
3838
private readonly bool trackResurrection;
3939
private readonly Converter<TItem, TKey> keyExtractor;
4040
private Dictionary<TKey, GCHandle> items;
@@ -46,7 +46,7 @@ public class WeakCache<TKey, TItem> :
4646
public Converter<TItem, TKey> KeyExtractor
4747
{
4848
[DebuggerStepThrough]
49-
get { return keyExtractor; }
49+
get => keyExtractor;
5050
}
5151

5252
/// <summary>
@@ -55,40 +55,32 @@ public Converter<TItem, TKey> KeyExtractor
5555
public bool TrackResurrection
5656
{
5757
[DebuggerStepThrough]
58-
get { return trackResurrection; }
58+
get => trackResurrection;
5959
}
6060

6161
/// <inheritdoc/>
6262
public int Count
6363
{
6464
[DebuggerStepThrough]
65-
get { return items.Count; }
65+
get => items?.Count ?? 0;
6666
}
6767

6868
#endregion
6969

7070
/// <inheritdoc/>
71-
public TItem this[TKey key, bool markAsHit]
72-
{
73-
get {
74-
TItem item;
75-
if (TryGetItem(key, markAsHit, out item))
76-
return item;
77-
else
78-
return null;
79-
}
80-
}
71+
public TItem this[TKey key, bool markAsHit] => TryGetItem(key, markAsHit, out var item) ? item : null;
8172

8273
/// <inheritdoc/>
8374
[SecuritySafeCritical]
8475
public virtual bool TryGetItem(TKey key, bool markAsHit, out TItem item)
8576
{
8677
RegisterOperation(1);
87-
GCHandle cached;
88-
if (items.TryGetValue(key, out cached)) {
89-
item = (TItem) cached.Target;
90-
if (item != null)
78+
if (items != null && items.TryGetValue(key, out var cached)) {
79+
item = ExtractTarget(cached);
80+
if (item != null) {
9181
return true;
82+
}
83+
9284
items.Remove(key);
9385
cached.Free();
9486
return false;
@@ -98,113 +90,105 @@ public virtual bool TryGetItem(TKey key, bool markAsHit, out TItem item)
9890
}
9991

10092
/// <inheritdoc/>
101-
public bool Contains(TItem item)
102-
{
103-
return ContainsKey(KeyExtractor(item));
104-
}
93+
public bool Contains(TItem item) => ContainsKey(KeyExtractor(item));
10594

10695
/// <inheritdoc/>
107-
public bool ContainsKey(TKey key)
108-
{
109-
TItem item;
110-
return TryGetItem(key, false, out item);
111-
}
96+
public bool ContainsKey(TKey key) => TryGetItem(key, false, out var _);
11297

11398
#region Modification methods: Add, Remove, Clear
11499

115100
/// <inheritdoc/>
116-
public void Add(TItem item)
117-
{
118-
Add(item, true);
119-
}
101+
public void Add(TItem item) => Add(item, true);
102+
103+
private static Dictionary<TKey, GCHandle> CreateDictionary() => new Dictionary<TKey, GCHandle>();
120104

121105
/// <inheritdoc/>
122106
[SecuritySafeCritical]
123107
public virtual TItem Add(TItem item, bool replaceIfExists)
124108
{
125-
ArgumentValidator.EnsureArgumentNotNull(item, "item");
109+
ArgumentValidator.EnsureArgumentNotNull(item, nameof(item));
126110
RegisterOperation(2);
127111
var key = KeyExtractor(item);
128-
GCHandle cached;
129-
if (items.TryGetValue(key, out cached)) {
130-
if (!replaceIfExists) {
131-
var cachedItem = (TItem) cached.Target;
132-
if (cachedItem != null)
133-
return cachedItem;
112+
if (items == null) {
113+
items = CreateDictionary();
114+
}
115+
else if (replaceIfExists) {
116+
if (items.Remove(key, out var cached)) {
117+
cached.Free();
118+
}
119+
}
120+
else if (items.TryGetValue(key, out var cached)) {
121+
if (ExtractTarget(cached) is TItem cachedItem) {
122+
return cachedItem;
134123
}
135124
items.Remove(key);
136125
cached.Free();
137126
}
138-
items[key] = GCHandle.Alloc(item,
139-
trackResurrection ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak);
127+
items[key] = GCHandle.Alloc(item, trackResurrection ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak);
140128
return item;
141129
}
142130

143131
/// <inheritdoc/>
144132
public void Remove(TItem item)
145133
{
146-
ArgumentValidator.EnsureArgumentNotNull(item, "item");
134+
ArgumentValidator.EnsureArgumentNotNull(item, nameof(item));
147135
RemoveKey(KeyExtractor(item));
148136
}
149137

150138
/// <inheritdoc/>
151139
[SecuritySafeCritical]
152140
public virtual void RemoveKey(TKey key)
153141
{
154-
GCHandle cached;
155-
if (items.TryGetValue(key, out cached)) {
156-
items.Remove(key);
142+
if (items != null && items.Remove(key, out var cached) == true) {
157143
cached.Free();
158144
}
159145
}
160146

161147
/// <inheritdoc/>
162-
public void RemoveKey(TKey key, bool removeCompletely)
163-
{
164-
RemoveKey(key);
165-
}
148+
public void RemoveKey(TKey key, bool removeCompletely) => RemoveKey(key);
166149

167150
/// <inheritdoc/>
168151
[SecuritySafeCritical]
169152
public virtual void Clear()
170153
{
154+
if (items == null) {
155+
return;
156+
}
171157
try {
172-
foreach (var pair in items)
158+
foreach (var pair in items) {
173159
try {
174160
pair.Value.Free();
175161
}
176162
catch { }
163+
}
177164
}
178165
finally {
179-
items = new Dictionary<TKey, GCHandle>();
166+
items = null;
180167
time = 0;
181168
}
182169
}
183170

184171
/// <inheritdoc/>
185-
public void Invalidate()
186-
{
187-
Clear();
188-
}
172+
public void Invalidate() => Clear();
189173

190174
/// <inheritdoc/>
191175
[SecuritySafeCritical]
192176
public virtual void CollectGarbage()
193177
{
194-
int count = items.Count;
195-
if (count <= NoGcCount)
178+
var count = items?.Count ?? 0;
179+
if (count <= NoGcCount) {
196180
return;
181+
}
197182

198183
Exception error = null;
199184
int removedCount = 0;
200185
try {
201186
// Filtering
202-
var newItems = new Dictionary<TKey, GCHandle>();
203-
foreach (var pair in items) {
204-
var cached = pair.Value;
187+
var newItems = CreateDictionary();
188+
foreach (var (key, cached) in items) {
205189
var item = cached.Target;
206190
if (item != null)
207-
newItems.Add(pair.Key, cached);
191+
newItems.Add(key, cached);
208192
else
209193
cached.Free();
210194
}
@@ -233,26 +217,19 @@ public virtual void CollectGarbage()
233217

234218
/// <inheritdoc/>
235219
[DebuggerStepThrough]
236-
IEnumerator IEnumerable.GetEnumerator()
237-
{
238-
return GetEnumerator();
239-
}
220+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
240221

241222
/// <inheritdoc/>
242223
public virtual IEnumerator<TItem> GetEnumerator()
243224
{
244-
foreach (var pair in items) {
245-
var item = ExtractTarget(pair.Value);
246-
if (item != null)
225+
foreach (var pair in items ?? Enumerable.Empty<KeyValuePair<TKey, GCHandle>>()) {
226+
if (ExtractTarget(pair.Value) is TItem item)
247227
yield return item;
248228
}
249229
}
250230

251231
[SecuritySafeCritical]
252-
private static TItem ExtractTarget(GCHandle handle)
253-
{
254-
return (TItem) handle.Target;
255-
}
232+
private static TItem ExtractTarget(GCHandle handle) => (TItem) handle.Target;
256233

257234
#endregion
258235

@@ -261,11 +238,10 @@ private static TItem ExtractTarget(GCHandle handle)
261238
private void RegisterOperation(int weight)
262239
{
263240
time += weight;
264-
var count = items.Count;
265-
if (count <= NoGcCount)
266-
return;
267-
if (time > ((count << 1) + count))
241+
var count = items?.Count ?? 0;
242+
if (count > NoGcCount && time > (count << 1) + count) {
268243
CollectGarbage();
244+
}
269245
}
270246

271247
#endregion
@@ -283,7 +259,6 @@ public WeakCache(bool trackResurrection, Converter<TItem, TKey> keyExtractor)
283259
ArgumentValidator.EnsureArgumentNotNull(keyExtractor, "keyExtractor");
284260
this.trackResurrection = trackResurrection;
285261
this.keyExtractor = keyExtractor;
286-
items = new Dictionary<TKey, GCHandle>(1024);
287262
}
288263

289264
// Dispose pattern
@@ -294,14 +269,7 @@ public WeakCache(bool trackResurrection, Converter<TItem, TKey> keyExtractor)
294269
[SecuritySafeCritical]
295270
protected virtual void Dispose(bool disposing)
296271
{
297-
if (items != null) {
298-
try {
299-
Clear();
300-
}
301-
finally {
302-
items = null;
303-
}
304-
}
272+
Clear();
305273
}
306274

307275
/// <summary>

Orm/Xtensive.Orm/Core/Pair{T}.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace Xtensive.Core
1818
/// <typeparam name="T">The type of both stored values.</typeparam>
1919
[Serializable]
2020
[DebuggerDisplay("{First}, {Second}")]
21-
public struct Pair<T> :
21+
public readonly struct Pair<T> :
2222
IEquatable<Pair<T>>,
2323
IComparable<Pair<T>>
2424
{

Orm/Xtensive.Orm/Orm/Internals/FieldAccessors/EntityFieldAccessor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2008-2020 Xtensive LLC.
1+
// Copyright (C) 2008-2021 Xtensive LLC.
22
// This code is distributed under MIT license terms.
33
// See the License.txt file in the project root for more information.
44
// Created by: Alexey Gamzov
@@ -54,4 +54,4 @@ public override T GetValue(Persistent obj)
5454
return (T) (object) obj.Session.Query.SingleOrDefault(key);
5555
}
5656
}
57-
}
57+
}

Orm/Xtensive.Orm/Orm/Internals/Prefetch/GraphContainer.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,9 @@ private static bool AreAllForeignKeyColumnsLoaded(EntityState state, FieldInfo f
139139
return false;
140140
var tuple = state.Tuple;
141141
var fieldStateMap = tuple.GetFieldStateMap(TupleFieldState.Available);
142-
for (var i = 0; i < field.MappingInfo.Length; i++)
143-
if (!fieldStateMap[field.MappingInfo.Offset + i])
142+
var fieldMappingInfo = field.MappingInfo;
143+
for (var i = 0; i < fieldMappingInfo.Length; i++)
144+
if (!fieldStateMap[fieldMappingInfo.Offset + i])
144145
return false;
145146
return true;
146147
}

Orm/Xtensive.Orm/Orm/Internals/Prefetch/PrefetchHelper.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// Copyright (C) 2003-2010 Xtensive LLC.
2-
// All rights reserved.
3-
// For conditions of distribution and use, see license.
1+
// Copyright (C) 2009-2021 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
44
// Created by: Alexander Nikolaev
55
// Created: 2009.10.22
66

@@ -61,7 +61,6 @@ public static bool AddColumns(IEnumerable<ColumnInfo> candidateColumns,
6161
SortedDictionary<int, ColumnInfo> columns, TypeInfo type)
6262
{
6363
var result = false;
64-
var primaryIndex = type.Indexes.PrimaryIndex;
6564
foreach (var column in candidateColumns) {
6665
result = true;
6766
if (type.IsInterface == column.Field.DeclaringType.IsInterface)

Orm/Xtensive.Orm/Orm/Linq/Expressions/EntityFieldExpression.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2009-2020 Xtensive LLC.
1+
// Copyright (C) 2009-2021 Xtensive LLC.
22
// This code is distributed under MIT license terms.
33
// See the License.txt file in the project root for more information.
44
// Created by: Alexis Kochetov
@@ -165,7 +165,7 @@ public static EntityFieldExpression CreateEntityField(FieldInfo entityField, int
165165
var entityType = entityField.ValueType;
166166
var persistentType = entityField.ReflectedType.Model.Types[entityType];
167167

168-
ref var mappingInfo = ref entityField.mappingInfo;
168+
var mappingInfo = entityField.MappingInfo;
169169
var mapping = new Segment<int>(mappingInfo.Offset + offset, mappingInfo.Length);
170170
var keyFields = persistentType.Key.Fields;
171171
var keyExpression = KeyExpression.Create(persistentType, offset + mappingInfo.Offset);

Orm/Xtensive.Orm/Orm/Linq/Expressions/FieldExpression.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ public static FieldExpression CreateField(FieldInfo field, int offset)
126126
throw new ArgumentException(string.Format(Strings.ExFieldXIsNotPrimitive, field.Name), nameof(field));
127127
}
128128

129-
ref var mappingInfo = ref field.mappingInfo;
129+
var mappingInfo = field.MappingInfo;
130130
var mapping = new Segment<int>(mappingInfo.Offset + offset, mappingInfo.Length);
131131
return new FieldExpression(ExtendedExpressionType.Field, field, mapping, null, false);
132132
}

Orm/Xtensive.Orm/Orm/Linq/Expressions/StructureFieldExpression.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2009-2020 Xtensive LLC.
1+
// Copyright (C) 2009-2021 Xtensive LLC.
22
// This code is distributed under MIT license terms.
33
// See the License.txt file in the project root for more information.
44
// Created by: Alexis Kochetov
@@ -176,12 +176,13 @@ public static StructureFieldExpression CreateStructure(FieldInfo structureField,
176176
}
177177

178178
var persistentType = structureField.ReflectedType.Model.Types[structureField.ValueType];
179-
var mapping = new Segment<int>(offset + structureField.MappingInfo.Offset, structureField.MappingInfo.Length);
179+
var fieldMappingInfo = structureField.MappingInfo;
180+
var mapping = new Segment<int>(offset + fieldMappingInfo.Offset, fieldMappingInfo.Length);
180181
var result = new StructureFieldExpression(persistentType, structureField, mapping, null, false);
181182
var processedFields = new List<PersistentFieldExpression>(persistentType.Fields.Count);
182183
foreach (var field in persistentType.Fields) {
183184
// Do not convert to LINQ. We want to avoid a closure creation here.
184-
processedFields.Add(BuildNestedFieldExpression(field, offset + structureField.MappingInfo.Offset));
185+
processedFields.Add(BuildNestedFieldExpression(field, offset + fieldMappingInfo.Offset));
185186
}
186187

187188
result.Fields = processedFields;

0 commit comments

Comments
 (0)