Skip to content

Commit 803fb2a

Browse files
authored
Done (#602)
1 parent d21f699 commit 803fb2a

File tree

14 files changed

+203
-114
lines changed

14 files changed

+203
-114
lines changed

src/aspnet/StackExchange.Redis.Extensions.AspNetCore/Extensions/IServiceCollectionExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public static IServiceCollection AddStackExchangeRedisExtensions<T>(
2626
RedisConfiguration redisConfiguration)
2727
where T : class, ISerializer
2828
{
29-
return services.AddStackExchangeRedisExtensions<T>(sp => new[] { redisConfiguration });
29+
return services.AddStackExchangeRedisExtensions<T>(sp => [redisConfiguration]);
3030
}
3131

3232
/// <summary>

src/core/StackExchange.Redis.Extensions.Core/Configuration/RedisConfiguration.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public class RedisConfiguration
2828
private int syncTimeout = 1000;
2929
private bool abortOnConnectFail;
3030
private int database;
31-
private RedisHost[] hosts = Array.Empty<RedisHost>();
31+
private RedisHost[] hosts = [];
3232
private ServerEnumerationStrategy serverEnumerationStrategy = new();
3333
private uint maxValueLength;
3434
private int poolSize = 5;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
using StackExchange.Redis.Extensions.Core.Models;
5+
6+
namespace StackExchange.Redis.Extensions.Core.Extensions;
7+
8+
internal static class SpanExtensions
9+
{
10+
public static void EnumerateLines(this ReadOnlySpan<char> span, ref List<InfoDetail> data, ref string category)
11+
{
12+
var start = 0;
13+
14+
while (start < span.Length)
15+
{
16+
var end = span[start..].IndexOf('\n');
17+
ReadOnlySpan<char> line;
18+
19+
if (end == -1)
20+
{
21+
line = span[start..].Trim();
22+
start = span.Length; // Termina il loop
23+
}
24+
else
25+
{
26+
line = span[start..(start + end)].Trim();
27+
start += end + 1;
28+
}
29+
30+
// Gestisci ogni riga
31+
if (line.IsEmpty)
32+
continue;
33+
34+
if (line[0] == '#')
35+
{
36+
category = line[1..].Trim().ToString();
37+
continue;
38+
}
39+
40+
var idx = line.IndexOf(':');
41+
if (idx > 0)
42+
{
43+
var key = line[..idx].Trim();
44+
var infoValue = line[(idx + 1)..].Trim();
45+
46+
data.Add(new InfoDetail(category, key.ToString(), infoValue.ToString()));
47+
}
48+
}
49+
}
50+
}

src/core/StackExchange.Redis.Extensions.Core/Extensions/ValueLengthExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ public static IEnumerable<KeyValuePair<string, byte[]>> OfValueInListSize<T>(thi
2525
public static byte[] OfValueSize<T>(this T? value, ISerializer serializer, uint maxValueLength, string key)
2626
{
2727
return value == null
28-
? Array.Empty<byte>()
28+
? []
2929
: serializer.Serialize(value).CheckLength(maxValueLength, key);
3030
}
3131

3232
private static byte[] SerializeItem<T>(this T? item, ISerializer serializer)
3333
{
3434
return item == null
35-
? Array.Empty<byte>()
35+
? []
3636
: serializer.Serialize(item);
3737
}
3838

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Runtime.CompilerServices;
4+
using System.Runtime.InteropServices;
5+
6+
namespace StackExchange.Redis.Extensions.Core.Helpers;
7+
8+
internal static class GenericsExtensions
9+
{
10+
public static void FastIteration<TSource>(this ICollection<TSource>? request, Action<TSource, int> action)
11+
{
12+
if (request == null)
13+
return;
14+
15+
if (request is TSource[] sourceArray)
16+
{
17+
ref var searchSpace = ref MemoryMarshal.GetReference(sourceArray.AsSpan());
18+
19+
for (var i = 0; i < sourceArray.Length; i++)
20+
{
21+
ref var r = ref Unsafe.Add(ref searchSpace, i);
22+
23+
action.Invoke(r, i);
24+
}
25+
}
26+
else
27+
{
28+
var i = 0;
29+
foreach (var r in request)
30+
action.Invoke(r, i++);
31+
}
32+
}
33+
34+
public static TResult[] ToFastArray<TSource, TResult>(this ICollection<TSource>? request, Func<TSource, TResult> action)
35+
{
36+
if (request == null)
37+
return [];
38+
39+
var result = new TResult[request.Count];
40+
41+
if (request is TSource[] sourceArray)
42+
{
43+
ref var searchSpace = ref MemoryMarshal.GetReference(sourceArray.AsSpan());
44+
45+
for (var i = 0; i < sourceArray.Length; i++)
46+
{
47+
ref var r = ref Unsafe.Add(ref searchSpace, i);
48+
49+
result[i] = action.Invoke(r);
50+
}
51+
}
52+
/*
53+
54+
This could be helpful when we drop old frameworks
55+
56+
else if (request is List<TSource> sourceList)
57+
{
58+
var span = CollectionsMarshal.AsSpan(sourceList);
59+
ref var searchSpace = ref MemoryMarshal.GetReference(span);
60+
61+
for (var i = 0; i < span.Length; i++)
62+
{
63+
ref var r = ref Unsafe.Add(ref searchSpace, i);
64+
65+
result[i] = action.Invoke(r);
66+
}
67+
}
68+
*/
69+
else
70+
{
71+
var i = 0;
72+
foreach (var r in request)
73+
result[i++] = action.Invoke(r);
74+
}
75+
76+
return result;
77+
}
78+
}

src/core/StackExchange.Redis.Extensions.Core/Implementations/RedisClientFactory.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
using System;
44
using System.Collections.Generic;
55
using System.Linq;
6+
using System.Runtime.CompilerServices;
7+
using System.Runtime.InteropServices;
68

79
using Microsoft.Extensions.Logging;
810
using Microsoft.Extensions.Logging.Abstractions;
@@ -36,9 +38,11 @@ public RedisClientFactory(IEnumerable<RedisConfiguration> configurations, ILogge
3638
if (redisConfigurations.Length == 1)
3739
redisConfigurations[0].IsDefault = true;
3840

41+
ref var searchSpace = ref MemoryMarshal.GetReference(redisConfigurations.AsSpan());
42+
3943
for (var i = 0; i < redisConfigurations.Length; i++)
4044
{
41-
var configuration = redisConfigurations[i];
45+
ref var configuration = ref Unsafe.Add(ref searchSpace, i);
4246

4347
if (configuration.IsDefault && hasDefaultConfigured)
4448
throw new ArgumentException("There is more than one default configuration. Only one default configuration is allowed.");
@@ -70,7 +74,7 @@ public RedisClientFactory(IEnumerable<RedisConfiguration> configurations, ILogge
7074

7175
for (var i = 0; i < redisConfigurations.Length; i++)
7276
{
73-
var configuration = redisConfigurations[i];
77+
ref var configuration = ref Unsafe.Add(ref searchSpace, i);
7478

7579
var poolManager = new RedisConnectionPoolManager(configuration, poolManagerLogger);
7680

src/core/StackExchange.Redis.Extensions.Core/Implementations/RedisConnectionPoolManager.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
using System.Collections.Generic;
55
using System.ComponentModel;
66
using System.Globalization;
7+
using System.Runtime.CompilerServices;
8+
using System.Runtime.InteropServices;
79
using System.Security.Cryptography;
810
/* Unmerged change from project 'StackExchange.Redis.Extensions.Core(net6.0)'
911
Before:
@@ -107,7 +109,7 @@ var nextIdx
107109
break;
108110

109111
case ConnectionSelectionStrategy.LeastLoaded:
110-
connection = ValueLengthExtensions.MinBy(connections, x => x.TotalOutstanding());
112+
connection = connections.MinBy(x => x.TotalOutstanding());
111113
break;
112114

113115
default:
@@ -133,8 +135,12 @@ public ConnectionPoolInformation GetConnectionInformation()
133135
var activeConnections = 0;
134136
var invalidConnections = 0;
135137

136-
foreach (var connection in connections)
138+
ref var searchSpace = ref MemoryMarshal.GetReference(connections.AsSpan());
139+
140+
for (var i = 0; i < connections.Length; i++)
137141
{
142+
ref var connection = ref Unsafe.Add(ref searchSpace, i);
143+
138144
if (!connection.IsConnected())
139145
{
140146
invalidConnections++;

src/core/StackExchange.Redis.Extensions.Core/Implementations/RedisDatabase.Hash.cs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44
using System.Collections.Concurrent;
55
using System.Collections.Generic;
66
using System.Linq;
7+
using System.Runtime.CompilerServices;
8+
using System.Runtime.InteropServices;
79
using System.Threading.Tasks;
810

11+
using StackExchange.Redis.Extensions.Core.Helpers;
12+
913
namespace StackExchange.Redis.Extensions.Core.Implementations;
1014

1115
public partial class RedisDatabase
@@ -65,8 +69,13 @@ await Parallel.ForEachAsync(keys, async (key, _) =>
6569

6670
var result = new Dictionary<string, T?>();
6771

72+
ref var searchSpace = ref MemoryMarshal.GetReference(tasks.AsSpan());
73+
6874
for (var i = 0; i < tasks.Length; i++)
69-
result.Add(keys[i], tasks[i].Result);
75+
{
76+
ref var task = ref Unsafe.Add(ref searchSpace, i);
77+
result.Add(keys[i], task.Result);
78+
}
7079

7180
return result;
7281
#endif
@@ -77,12 +86,7 @@ await Parallel.ForEachAsync(keys, async (key, _) =>
7786
{
7887
var luascript = "local results = {};local insert = table.insert;local rcall = redis.call;for i=1,table.getn(KEYS) do local value = rcall('HGET','" + hashKey + "', KEYS[i]) if value then insert(results, KEYS[i]) insert(results, value) end end; return results;";
7988

80-
var list = new List<RedisKey>();
81-
82-
foreach (var key in keys)
83-
list.Add(new RedisKey(key));
84-
85-
var redisKeys = list.ToArray();
89+
var redisKeys = keys.ToFastArray(key => new RedisKey(key));
8690

8791
var data = await Database.ScriptEvaluateAsync(luascript, redisKeys, flags: flag).ConfigureAwait(false);
8892

@@ -93,12 +97,14 @@ await Parallel.ForEachAsync(keys, async (key, _) =>
9397

9498
var redisValues = ((RedisValue[]?)data);
9599

96-
if (redisValues == null || redisValues.Length <= 0)
100+
ref var searchSpaceRedisValue = ref MemoryMarshal.GetReference(redisValues.AsSpan());
101+
102+
if (redisValues is not { Length: > 0 })
97103
return dictionary;
98104

99105
for (var i = 0; i < redisValues.Length; i += 2)
100106
{
101-
var key = redisValues[i];
107+
ref var key = ref Unsafe.Add(ref searchSpaceRedisValue, i);
102108

103109
if (key.HasValue == false)
104110
continue;

src/core/StackExchange.Redis.Extensions.Core/Implementations/RedisDatabase.List.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
// Copyright (c) Ugo Lattanzi. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
22

33
using System;
4-
using System.Linq;
54
using System.Threading.Tasks;
65

6+
using StackExchange.Redis.Extensions.Core.Helpers;
7+
78
namespace StackExchange.Redis.Extensions.Core.Implementations;
89

910
public partial class RedisDatabase
@@ -31,7 +32,7 @@ public Task<long> ListAddToLeftAsync<T>(string key, T[] items, CommandFlags flag
3132
if (items == null)
3233
throw new ArgumentNullException(nameof(items), "item cannot be null.");
3334

34-
var serializedItems = items.Select(x => (RedisValue)Serializer.Serialize(x)).ToArray();
35+
var serializedItems = items.ToFastArray(item => (RedisValue)Serializer.Serialize(item));
3536

3637
return Database.ListLeftPushAsync(key, serializedItems, flag);
3738
}

src/core/StackExchange.Redis.Extensions.Core/Implementations/RedisDatabase.PubSub.cs

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
using System.Collections.Generic;
55
using System.Threading.Tasks;
66

7+
using StackExchange.Redis.Extensions.Core.Helpers;
8+
79
namespace StackExchange.Redis.Extensions.Core.Implementations;
810

911
public partial class RedisDatabase
@@ -67,51 +69,27 @@ public async Task<bool> UpdateExpiryAsync(string key, TimeSpan expiresIn, Comman
6769
/// <inheritdoc/>
6870
public async Task<IDictionary<string, bool>> UpdateExpiryAllAsync(HashSet<string> keys, DateTimeOffset expiresAt, CommandFlags flag = CommandFlags.None)
6971
{
70-
var tasks = new Task<bool>[keys.Count];
71-
72-
var i = 0;
73-
foreach (var key in keys)
74-
{
75-
tasks[i] = UpdateExpiryAsync(key, expiresAt.UtcDateTime, flag);
76-
i++;
77-
}
72+
var tasks = keys.ToFastArray(key => UpdateExpiryAsync(key, expiresAt.UtcDateTime, flag));
7873

7974
await Task.WhenAll(tasks).ConfigureAwait(false);
8075

8176
var results = new Dictionary<string, bool>(keys.Count, StringComparer.Ordinal);
8277

83-
i = 0;
84-
foreach (var key in keys)
85-
{
86-
results.Add(key, tasks[i].Result);
87-
i++;
88-
}
78+
keys.FastIteration((key, i) => results.Add(key, tasks[i].Result));
8979

9080
return results;
9181
}
9282

9383
/// <inheritdoc/>
9484
public async Task<IDictionary<string, bool>> UpdateExpiryAllAsync(HashSet<string> keys, TimeSpan expiresIn, CommandFlags flag = CommandFlags.None)
9585
{
96-
var tasks = new Task<bool>[keys.Count];
97-
98-
var i = 0;
99-
foreach (var key in keys)
100-
{
101-
tasks[i] = UpdateExpiryAsync(key, expiresIn, flag);
102-
i++;
103-
}
86+
var tasks = keys.ToFastArray(key => UpdateExpiryAsync(key, expiresIn, flag));
10487

10588
await Task.WhenAll(tasks).ConfigureAwait(false);
10689

10790
var results = new Dictionary<string, bool>(keys.Count, StringComparer.Ordinal);
10891

109-
i = 0;
110-
foreach (var key in keys)
111-
{
112-
results.Add(key, tasks[i].Result);
113-
i++;
114-
}
92+
keys.FastIteration((key, i) => results.Add(key, tasks[i].Result));
11593

11694
return results;
11795
}

0 commit comments

Comments
 (0)