Skip to content

Commit 16a866f

Browse files
committed
move FP32 logic to the request level
1 parent b1c6399 commit 16a866f

7 files changed

Lines changed: 120 additions & 161 deletions

File tree

src/StackExchange.Redis/VectorSetAddMessage.cs

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ internal abstract class VectorSetAddMessage(
1212
VectorSetQuantization quantization,
1313
int? buildExplorationFactor,
1414
int? maxConnections,
15-
bool useCheckAndSet) : Message(db, flags, RedisCommand.VADD)
15+
bool useCheckAndSet,
16+
bool useFp32) : Message(db, flags, RedisCommand.VADD)
1617
{
17-
public override int ArgCount => GetArgCount(UseFp32);
18+
public override int ArgCount => GetArgCount();
1819

19-
private int GetArgCount(bool packed)
20+
private int GetArgCount()
2021
{
21-
var count = 2 + GetElementArgCount(packed); // key, element and either "FP32 {vector}" or VALUES {num}"
22+
var count = 2 + GetElementArgCount(); // key, element and either "FP32 {vector}" or VALUES {num}"
2223
if (reducedDimensions.HasValue) count += 2; // [REDUCE {dim}]
2324

2425
if (useCheckAndSet) count++; // [CAS]
@@ -35,45 +36,34 @@ private int GetArgCount(bool packed)
3536
return count;
3637
}
3738

38-
public abstract int GetElementArgCount(bool packed);
39+
public abstract int GetElementArgCount();
3940
public abstract int GetAttributeArgCount();
4041

4142
public override int GetHashSlot(ServerSelectionStrategy serverSelectionStrategy)
4243
=> serverSelectionStrategy.HashSlot(key);
4344

44-
private static readonly bool CanUseFp32 = BitConverter.IsLittleEndian && CheckFp32();
45+
internal static readonly bool CanUseFp32 = BitConverter.IsLittleEndian && CheckFp32();
46+
internal bool UseFp32 { get; } = useFp32 & CanUseFp32; // evaluated during .ctor
4547

4648
private static bool CheckFp32() // check endianness with a known value
4749
{
4850
// ReSharper disable once CompareOfFloatsByEqualityOperator - expect exact
4951
return MemoryMarshal.Cast<byte, float>("\0\0(B"u8)[0] == 42;
5052
}
5153

52-
#if DEBUG
53-
private static int _fp32Disabled;
54-
internal static bool UseFp32 => CanUseFp32 & Volatile.Read(ref _fp32Disabled) == 0;
55-
internal static void SuppressFp32() => Interlocked.Increment(ref _fp32Disabled);
56-
internal static void RestoreFp32() => Interlocked.Decrement(ref _fp32Disabled);
57-
#else
58-
internal static bool UseFp32 => CanUseFp32;
59-
internal static void SuppressFp32() { }
60-
internal static void RestoreFp32() { }
61-
#endif
62-
63-
protected abstract void WriteElement(bool packed, PhysicalConnection physical);
54+
protected abstract void WriteElement(PhysicalConnection physical);
6455

6556
protected override void WriteImpl(PhysicalConnection physical)
6657
{
67-
bool packed = UseFp32; // snapshot to avoid race in debug scenarios
68-
physical.WriteHeader(Command, GetArgCount(packed));
58+
physical.WriteHeader(Command, GetArgCount());
6959
physical.Write(key);
7060
if (reducedDimensions.HasValue)
7161
{
7262
physical.WriteBulkString("REDUCE"u8);
7363
physical.WriteBulkString(reducedDimensions.GetValueOrDefault());
7464
}
7565

76-
WriteElement(packed, physical);
66+
WriteElement(physical);
7767
if (useCheckAndSet) physical.WriteBulkString("CAS"u8);
7868

7969
switch (quantization)
@@ -118,27 +108,29 @@ internal sealed class VectorSetAddMemberMessage(
118108
bool useCheckAndSet,
119109
RedisValue element,
120110
ReadOnlyMemory<float> values,
121-
string? attributesJson) : VectorSetAddMessage(
111+
string? attributesJson,
112+
bool useFp32) : VectorSetAddMessage(
122113
db,
123114
flags,
124115
key,
125116
reducedDimensions,
126117
quantization,
127118
buildExplorationFactor,
128119
maxConnections,
129-
useCheckAndSet)
120+
useCheckAndSet,
121+
useFp32)
130122
{
131123
private readonly string? _attributesJson = string.IsNullOrWhiteSpace(attributesJson) ? null : attributesJson;
132-
public override int GetElementArgCount(bool packed)
124+
public override int GetElementArgCount()
133125
=> 2 // "FP32 {vector}" or "VALUES {num}"
134-
+ (packed ? 0 : values.Length); // {vector...}"
126+
+ (UseFp32 ? 0 : values.Length); // {vector...}"
135127

136128
public override int GetAttributeArgCount()
137129
=> _attributesJson is null ? 0 : 2; // [SETATTR {attributes}]
138130

139-
protected override void WriteElement(bool packed, PhysicalConnection physical)
131+
protected override void WriteElement(PhysicalConnection physical)
140132
{
141-
if (packed)
133+
if (UseFp32)
142134
{
143135
physical.WriteBulkString("FP32"u8);
144136
physical.WriteBulkString(MemoryMarshal.AsBytes(values.Span));

src/StackExchange.Redis/VectorSetAddRequest.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public static VectorSetAddRequest Member(
3838
/// </summary>
3939
public int? ReducedDimensions { get; set; }
4040

41+
internal bool UseFp32 { get; set; } = true; // for testing
42+
4143
/// <summary>
4244
/// Quantization type - Int8 (Q8), None (NOQUANT), or Binary (BIN). Default: Int8.
4345
/// </summary>
@@ -74,6 +76,7 @@ internal override VectorSetAddMessage ToMessage(RedisKey key, int db, CommandFla
7476
UseCheckAndSet,
7577
element,
7678
values,
77-
attributesJson);
79+
attributesJson,
80+
UseFp32);
7881
}
7982
}

src/StackExchange.Redis/VectorSetSimilaritySearchMessage.cs

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ internal abstract class VectorSetSimilaritySearchMessage(
1111
double epsilon,
1212
int searchExplorationFactor,
1313
string? filterExpression,
14-
int maxFilteringEffort) : Message(db, flags, RedisCommand.VSIM)
14+
int maxFilteringEffort,
15+
bool useFp32) : Message(db, flags, RedisCommand.VSIM)
1516
{
1617
// For "FP32" and "VALUES" scenarios; in the future we might want other vector sizes / encodings - for
1718
// example, there could be some "FP16" or "FP8" transport that requires a ROM-short or ROM-sbyte from
@@ -27,15 +28,16 @@ internal sealed class VectorSetSimilaritySearchBySingleVectorMessage(
2728
double epsilon,
2829
int searchExplorationFactor,
2930
string? filterExpression,
30-
int maxFilteringEffort) : VectorSetSimilaritySearchMessage(db, flags, vsimFlags, key, count, epsilon,
31-
searchExplorationFactor, filterExpression, maxFilteringEffort)
31+
int maxFilteringEffort,
32+
bool useFp32) : VectorSetSimilaritySearchMessage(db, flags, vsimFlags, key, count, epsilon,
33+
searchExplorationFactor, filterExpression, maxFilteringEffort, useFp32)
3234
{
33-
internal override int GetSearchTargetArgCount(bool packed) =>
34-
packed ? 2 : 2 + vector.Length; // FP32 {vector} or VALUES {num} {vector}
35+
internal override int GetSearchTargetArgCount() =>
36+
UseFp32 ? 2 : (2 + vector.Length); // FP32 {vector} or VALUES {num} {vector}
3537

36-
internal override void WriteSearchTarget(bool packed, PhysicalConnection physical)
38+
internal override void WriteSearchTarget(PhysicalConnection physical)
3739
{
38-
if (packed)
40+
if (UseFp32)
3941
{
4042
physical.WriteBulkString("FP32"u8);
4143
physical.WriteBulkString(System.Runtime.InteropServices.MemoryMarshal.AsBytes(vector.Span));
@@ -63,20 +65,21 @@ internal sealed class VectorSetSimilaritySearchByMemberMessage(
6365
double epsilon,
6466
int searchExplorationFactor,
6567
string? filterExpression,
66-
int maxFilteringEffort) : VectorSetSimilaritySearchMessage(db, flags, vsimFlags, key, count, epsilon,
67-
searchExplorationFactor, filterExpression, maxFilteringEffort)
68+
int maxFilteringEffort,
69+
bool useFp32) : VectorSetSimilaritySearchMessage(db, flags, vsimFlags, key, count, epsilon,
70+
searchExplorationFactor, filterExpression, maxFilteringEffort, useFp32)
6871
{
69-
internal override int GetSearchTargetArgCount(bool packed) => 2; // ELE {member}
72+
internal override int GetSearchTargetArgCount() => 2; // ELE {member}
7073

71-
internal override void WriteSearchTarget(bool packed, PhysicalConnection physical)
74+
internal override void WriteSearchTarget(PhysicalConnection physical)
7275
{
7376
physical.WriteBulkString("ELE"u8);
7477
physical.WriteBulkString(member);
7578
}
7679
}
7780

78-
internal abstract int GetSearchTargetArgCount(bool packed);
79-
internal abstract void WriteSearchTarget(bool packed, PhysicalConnection physical);
81+
internal abstract int GetSearchTargetArgCount();
82+
internal abstract void WriteSearchTarget(PhysicalConnection physical);
8083

8184
public ResultProcessor<Lease<VectorSetSimilaritySearchResult>?> GetResultProcessor() =>
8285
VectorSetSimilaritySearchProcessor.Instance;
@@ -177,11 +180,11 @@ internal enum VsimFlags
177180

178181
private bool HasFlag(VsimFlags flag) => (vsimFlags & flag) != 0;
179182

180-
public override int ArgCount => GetArgCount(VectorSetAddMessage.UseFp32);
183+
public override int ArgCount => GetArgCount();
181184

182-
private int GetArgCount(bool packed)
185+
private int GetArgCount()
183186
{
184-
int argCount = 1 + GetSearchTargetArgCount(packed); // {key} and whatever we need for the vector/element portion
187+
int argCount = 1 + GetSearchTargetArgCount(); // {key} and whatever we need for the vector/element portion
185188
if (HasFlag(VsimFlags.WithScores)) argCount++; // [WITHSCORES]
186189
if (HasFlag(VsimFlags.WithAttributes)) argCount++; // [WITHATTRIBS]
187190
if (HasFlag(VsimFlags.Count)) argCount += 2; // [COUNT {count}]
@@ -194,17 +197,18 @@ private int GetArgCount(bool packed)
194197
return argCount;
195198
}
196199

200+
internal bool UseFp32 { get; } = useFp32 & VectorSetAddMessage.CanUseFp32; // evaluated during .ctor
201+
197202
protected override void WriteImpl(PhysicalConnection physical)
198203
{
199204
// snapshot to avoid race in debug scenarios
200-
bool packed = VectorSetAddMessage.UseFp32;
201-
physical.WriteHeader(Command, GetArgCount(packed));
205+
physical.WriteHeader(Command, GetArgCount());
202206

203207
// Write key
204208
physical.Write(key);
205209

206210
// Write search target: either "ELE {member}" or vector data
207-
WriteSearchTarget(packed, physical);
211+
WriteSearchTarget(physical);
208212

209213
if (HasFlag(VsimFlags.WithScores))
210214
{

src/StackExchange.Redis/VectorSetSimilaritySearchRequest.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ internal override VectorSetSimilaritySearchMessage ToMessage(RedisKey key, int d
2626
_epsilon,
2727
_searchExplorationFactor,
2828
_filterExpression,
29-
_maxFilteringEffort);
29+
_maxFilteringEffort,
30+
UseFp32);
3031
}
3132

3233
private sealed class VectorSetSimilarityVectorSingleSearchRequest(ReadOnlyMemory<float> vector)
@@ -43,9 +44,12 @@ internal override VectorSetSimilaritySearchMessage ToMessage(RedisKey key, int d
4344
_epsilon,
4445
_searchExplorationFactor,
4546
_filterExpression,
46-
_maxFilteringEffort);
47+
_maxFilteringEffort,
48+
UseFp32);
4749
}
4850

51+
internal bool UseFp32 { get; set; } = true; // for testing
52+
4953
// snapshot the values; I don't trust people not to mutate the object behind my back
5054
internal abstract VectorSetSimilaritySearchMessage ToMessage(RedisKey key, int db, CommandFlags flags);
5155

tests/StackExchange.Redis.Tests/KeyPrefixedVectorSetTests.cs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,11 @@ public void VectorSetAdd_Fp32()
2222
{
2323
if (BitConverter.IsLittleEndian)
2424
{
25-
Assert.True(VectorSetAddMessage.UseFp32);
26-
#if DEBUG // can be suppressed
27-
VectorSetAddMessage.SuppressFp32();
28-
Assert.False(VectorSetAddMessage.UseFp32);
29-
VectorSetAddMessage.RestoreFp32();
30-
Assert.True(VectorSetAddMessage.UseFp32);
31-
#endif
25+
Assert.True(VectorSetAddMessage.CanUseFp32);
3226
}
3327
else
3428
{
35-
Assert.False(VectorSetAddMessage.UseFp32);
29+
Assert.False(VectorSetAddMessage.CanUseFp32);
3630
}
3731
}
3832

0 commit comments

Comments
 (0)