Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8127004
Initial BITOP refactor
PaulusParssinen Jan 22, 2025
e691692
Remove TensorPrimitives invoke logic for now
PaulusParssinen Jan 22, 2025
ca04c55
Merge branch 'main' into bitop
PaulusParssinen Jan 26, 2025
810b8af
format whitespace & remove incorrect todo comment
PaulusParssinen Jan 22, 2025
693f504
Remove unnecessary usings
PaulusParssinen Jan 22, 2025
5aa410e
Add HW acceleration guards and V128 core path for non V256 CPUs
PaulusParssinen Mar 12, 2025
c4d7fc2
Merge branch 'main' into bitop
PaulusParssinen Mar 12, 2025
4a4c259
fix
PaulusParssinen Mar 13, 2025
2928ff7
Add S.N.TensorPrimitives and use it for BITOP NOT
PaulusParssinen Mar 18, 2025
bfcbbdb
Use TensorPrimitives to specialize two key scenario
PaulusParssinen Mar 18, 2025
e9acc2f
Make BitOp tests more granular with parameterization
PaulusParssinen Mar 23, 2025
b21aa39
Merge branch 'main' into bitop
PaulusParssinen Apr 2, 2025
6c7eb04
Merge branch 'main' into bitop
PaulusParssinen May 8, 2025
365d9f9
Make uneven 2-input specialization fallback to the multi-key path for…
PaulusParssinen May 8, 2025
ba37ada
Simplify bitmap operation result memory logic
PaulusParssinen May 9, 2025
1d56721
Add out-of-proc tests for the bitop HW accelerated paths
PaulusParssinen May 10, 2025
ebd0192
Add benchmarks
PaulusParssinen May 19, 2025
d772183
dotnet format
PaulusParssinen May 19, 2025
52140a7
Merge branch 'main' into bitop
PaulusParssinen Sep 17, 2025
3e62352
Add Vector512 path
PaulusParssinen Sep 17, 2025
1576d35
Handle environment variables containing '='
PaulusParssinen Sep 17, 2025
95b4ae0
Merge branch 'main' into bitop
PaulusParssinen Sep 28, 2025
292f650
Remove the two-input special path
PaulusParssinen Oct 1, 2025
21ed296
Add BITOP DIFF support
PaulusParssinen Oct 1, 2025
1c0d084
Wire up DIFF with rest of the BITOPs
PaulusParssinen Oct 2, 2025
4693c48
forgot to set the Lua registry constant
PaulusParssinen Oct 2, 2025
70993bf
Merge branch 'main' into bitop
vazois Oct 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="8.6.1" />
<PackageVersion Include="Microsoft.IdentityModel.Validators" Version="8.6.1" />
<PackageVersion Include="StackExchange.Redis" Version="2.8.16" />
<PackageVersion Include="StackExchange.Redis" Version="2.9.25" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.6.1" />
<PackageVersion Include="System.Interactive.Async" Version="6.0.1" />
<PackageVersion Include="System.Text.Json" Version="9.0.3" />
<PackageVersion Include="System.Numerics.Tensors" Version="9.0.9" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.8" />
</ItemGroup>
Expand Down
81 changes: 81 additions & 0 deletions benchmark/BDN.benchmark/Bitmap/BinaryOperations.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes;
using Garnet.server;

namespace BDN.benchmark.Bitmap
{
public unsafe class BinaryOperations
{
private const int Alignment = 64;

[ParamsSource(nameof(GetBitmapSizes))]
public Sizes BitmapSizes { get; set; }

[Params(BitmapOperation.XOR)]
public BitmapOperation Op { get; set; }

public IEnumerable<Sizes> GetBitmapSizes()
{
yield return new([1 << 21, 1 << 21]);
yield return new([1 << 21, (1 << 21) + 1]);

yield return new([1 << 21, 1 << 21, 1 << 21]);
yield return new([1 << 21, 1 << 21, (1 << 21) + 1]);

yield return new([256, 6 * 512 + 7, 512, 1024]);
}

private int minBitmapSize;
private byte** srcPtrs;
private byte** srcEndPtrs;

private int dstLength;
private byte* dstPtr;

[GlobalSetup]
public void GlobalSetup_Binary()
{
minBitmapSize = BitmapSizes.Values.Min();
srcPtrs = (byte**)NativeMemory.AllocZeroed((nuint)BitmapSizes.Values.Length, (nuint)sizeof(byte*));
srcEndPtrs = (byte**)NativeMemory.AllocZeroed((nuint)BitmapSizes.Values.Length, (nuint)sizeof(byte*));

for (var i = 0; i < BitmapSizes.Values.Length; i++)
{
srcPtrs[i] = (byte*)NativeMemory.AlignedAlloc((nuint)BitmapSizes.Values[i], Alignment);
srcEndPtrs[i] = srcPtrs[i] + BitmapSizes.Values[i];

new Random(i).NextBytes(new Span<byte>(srcPtrs[i], BitmapSizes.Values[i]));
}

dstLength = BitmapSizes.Values.Max();
dstPtr = (byte*)NativeMemory.AlignedAlloc((nuint)dstLength, Alignment);
}

[Benchmark]
public void BinaryOperation()
{
BitmapManager.InvokeBitOperationUnsafe(Op, BitmapSizes.Values.Length, srcPtrs, srcEndPtrs, dstPtr, dstLength, minBitmapSize);
}

[GlobalCleanup]
public void GlobalCleanup()
{
for (var i = 0; i < BitmapSizes.Values.Length; i++)
{
NativeMemory.AlignedFree(srcPtrs[i]);
}

NativeMemory.Free(srcPtrs);
NativeMemory.Free(srcEndPtrs);
NativeMemory.AlignedFree(dstPtr);
}

public record struct Sizes(int[] Values)
{
public override string ToString() => string.Join(", ", Values);
}
}
}
59 changes: 59 additions & 0 deletions benchmark/BDN.benchmark/Bitmap/UnaryOperations.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes;
using Garnet.server;

namespace BDN.benchmark.Bitmap
{
public unsafe partial class UnaryOperations
{
private const int Alignment = 64;

[ParamsSource(nameof(GetBitmapSize))]
public int BitmapSize { get; set; }

public IEnumerable<int> GetBitmapSize()
{
yield return 256;
yield return 1 << 21;
}

private const int Keys = 1;
private byte** srcPtrs;
private byte** srcEndPtrs;

private byte* dstPtr;

[GlobalSetup]
public void GlobalSetup_Unary()
{
srcPtrs = (byte**)NativeMemory.AllocZeroed(Keys, (nuint)sizeof(byte*));
srcEndPtrs = (byte**)NativeMemory.AllocZeroed(Keys, (nuint)sizeof(byte*));

srcPtrs[0] = (byte*)NativeMemory.AlignedAlloc((uint)BitmapSize, Alignment);
srcEndPtrs[0] = srcPtrs[0] + (uint)BitmapSize;

new Random(0).NextBytes(new Span<byte>(srcPtrs[0], BitmapSize));

dstPtr = (byte*)NativeMemory.AlignedAlloc((nuint)BitmapSize, Alignment);
}

[Benchmark]
public void BitOperation_NOT()
{
BitmapManager.InvokeBitOperationUnsafe(BitmapOperation.NOT, Keys, srcPtrs, srcEndPtrs, dstPtr, BitmapSize, BitmapSize);
}

[GlobalCleanup]
public void GlobalCleanup()
{
NativeMemory.AlignedFree(srcPtrs[0]);

NativeMemory.Free(srcPtrs);
NativeMemory.Free(srcEndPtrs);
NativeMemory.AlignedFree(dstPtr);
}
}
}
80 changes: 80 additions & 0 deletions libs/common/Numerics/IBinaryOperator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

using System.Numerics;
using System.Runtime.Intrinsics;

namespace Garnet.common.Numerics
{
/// <summary>Operator that takes two input values and returns a single value.</summary>
public interface IBinaryOperator
{
/// <summary>
/// Computes the binary operation of two scalar values.
/// </summary>
static abstract T Invoke<T>(T x, T y) where T : IBinaryInteger<T>;

/// <summary>
/// Computes the binary operation of two vectors.
/// </summary>
static abstract Vector128<byte> Invoke(Vector128<byte> x, Vector128<byte> y);

/// <inheritdoc cref="Invoke(Vector128{byte}, Vector128{byte})"/>
static abstract Vector256<byte> Invoke(Vector256<byte> x, Vector256<byte> y);

/// <inheritdoc cref="Invoke(Vector128{byte}, Vector128{byte})"/>
static abstract Vector512<byte> Invoke(Vector512<byte> x, Vector512<byte> y);
}

/// <summary><c>x &amp; y</c></summary>
public readonly struct BitwiseAndOperator : IBinaryOperator
{
/// <inheritdoc/>
public static T Invoke<T>(T x, T y) where T : IBinaryInteger<T> => x & y;
/// <inheritdoc/>
public static Vector128<byte> Invoke(Vector128<byte> x, Vector128<byte> y) => x & y;
/// <inheritdoc/>
public static Vector256<byte> Invoke(Vector256<byte> x, Vector256<byte> y) => x & y;
/// <inheritdoc/>
public static Vector512<byte> Invoke(Vector512<byte> x, Vector512<byte> y) => x & y;
}

/// <summary><c>x | y</c></summary>
public readonly struct BitwiseOrOperator : IBinaryOperator
{
/// <inheritdoc/>
public static T Invoke<T>(T x, T y) where T : IBinaryInteger<T> => x | y;
/// <inheritdoc/>
public static Vector128<byte> Invoke(Vector128<byte> x, Vector128<byte> y) => x | y;
/// <inheritdoc/>
public static Vector256<byte> Invoke(Vector256<byte> x, Vector256<byte> y) => x | y;
/// <inheritdoc/>
public static Vector512<byte> Invoke(Vector512<byte> x, Vector512<byte> y) => x | y;
}

/// <summary><c>x ^ y</c></summary>
public readonly struct BitwiseXorOperator : IBinaryOperator
{
/// <inheritdoc/>
public static T Invoke<T>(T x, T y) where T : IBinaryInteger<T> => x ^ y;
/// <inheritdoc/>
public static Vector128<byte> Invoke(Vector128<byte> x, Vector128<byte> y) => x ^ y;
/// <inheritdoc/>
public static Vector256<byte> Invoke(Vector256<byte> x, Vector256<byte> y) => x ^ y;
/// <inheritdoc/>
public static Vector512<byte> Invoke(Vector512<byte> x, Vector512<byte> y) => x ^ y;
}

/// <summary><c>x &amp; ~y</c></summary>
public readonly struct BitwiseAndNotOperator : IBinaryOperator
{
/// <inheritdoc/>
public static T Invoke<T>(T x, T y) where T : IBinaryInteger<T> => x & ~y;
/// <inheritdoc/>
public static Vector128<byte> Invoke(Vector128<byte> x, Vector128<byte> y) => x & ~y;
/// <inheritdoc/>
public static Vector256<byte> Invoke(Vector256<byte> x, Vector256<byte> y) => x & ~y;
/// <inheritdoc/>
public static Vector512<byte> Invoke(Vector512<byte> x, Vector512<byte> y) => x & ~y;
}
}
1 change: 1 addition & 0 deletions libs/server/Garnet.server.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" />
<PackageReference Include="System.Numerics.Tensors" />
<PackageReference Include="KeraLua" />
</ItemGroup>

Expand Down
1 change: 1 addition & 0 deletions libs/server/Lua/LuaRunner.Functions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2857,6 +2857,7 @@ internal int AclCheckCommand(nint luaStatePtr)
case RespCommand.BITOP_OR: state.PushConstantString(constStrs.OR); break;
case RespCommand.BITOP_XOR: state.PushConstantString(constStrs.XOR); break;
case RespCommand.BITOP_NOT: state.PushConstantString(constStrs.NOT); break;
case RespCommand.BITOP_DIFF: state.PushConstantString(constStrs.DIFF); break;

default: throw new InvalidOperationException($"Unexpected BITOP sub command: {subCommand}");
}
Expand Down
3 changes: 3 additions & 0 deletions libs/server/Lua/LuaRunner.Strings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ private readonly struct ConstantStringRegistryIndexes
internal int XOR { get; }
/// <see cref="CmdStrings.LUA_NOT"/>
internal int NOT { get; }
/// <see cref="CmdStrings.LUA_DIFF"/>
internal int DIFF { get; }
/// <see cref="CmdStrings.LUA_KEYS"/>
internal int KEYS { get; }
/// <see cref="CmdStrings.LUA_ARGV"/>
Expand Down Expand Up @@ -246,6 +248,7 @@ internal ConstantStringRegistryIndexes(ref LuaStateWrapper state)
OR = ConstantStringToRegistry(ref state, CmdStrings.LUA_OR);
XOR = ConstantStringToRegistry(ref state, CmdStrings.LUA_XOR);
NOT = ConstantStringToRegistry(ref state, CmdStrings.LUA_NOT);
DIFF = ConstantStringToRegistry(ref state, CmdStrings.LUA_DIFF);
KEYS = ConstantStringToRegistry(ref state, CmdStrings.LUA_KEYS);
ARGV = ConstantStringToRegistry(ref state, CmdStrings.LUA_ARGV);
}
Expand Down
12 changes: 11 additions & 1 deletion libs/server/Resp/Bitmap/BitmapCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ public enum BitmapOperation : byte
/// <summary>
/// NOT
/// </summary>
NOT
NOT,

/// <summary>
/// DIFF
/// </summary>
DIFF
}

internal enum BitFieldOverflow : byte
Expand Down Expand Up @@ -317,6 +322,11 @@ private bool NetworkStringBitOperation<TGarnetApi>(BitmapOperation bitOp, ref TG
return AbortWithErrorMessage(CmdStrings.RESP_ERR_WRONG_NUMBER_OF_ARGUMENTS);
}

if (bitOp == BitmapOperation.DIFF && parseState.Count < 3)
{
return AbortWithErrorMessage(CmdStrings.RESP_ERR_BITOP_DIFF_TWO_SOURCE_KEYS_REQUIRED);
}

if (parseState.Count > 64)
{
return AbortWithErrorMessage(CmdStrings.RESP_ERR_BITOP_KEY_LIMIT);
Expand Down
Loading