Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 6 additions & 6 deletions tests/DotLLM.Tests.Unit/Cpu/Kernels/DequantizeKQuantTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,10 @@ public void Q6_K_SingleBlock_HandCalculated()
}
}

[Fact]
[SkippableFact]
public void Q6_K_ScalarMatchesAvx2_RandomBlocks()
{
if (!Avx2.IsSupported) return;
Skip.IfNot(Avx2.IsSupported, "Requires AVX2.");

const int blockCount = 8;
const int totalElements = blockCount * KQuantGroupSize;
Expand Down Expand Up @@ -237,10 +237,10 @@ public void Q4_K_SingleBlock_HandCalculated()
}
}

[Fact]
[SkippableFact]
public void Q4_K_ScalarMatchesAvx2_RandomBlocks()
{
if (!Avx2.IsSupported) return;
Skip.IfNot(Avx2.IsSupported, "Requires AVX2.");

const int blockCount = 8;
const int totalElements = blockCount * KQuantGroupSize;
Expand Down Expand Up @@ -312,10 +312,10 @@ public void Q5_K_SingleBlock_HandCalculated()
}
}

[Fact]
[SkippableFact]
public void Q5_K_ScalarMatchesAvx2_RandomBlocks()
{
if (!Avx2.IsSupported) return;
Skip.IfNot(Avx2.IsSupported, "Requires AVX2.");

const int blockCount = 8;
const int totalElements = blockCount * KQuantGroupSize;
Expand Down
4 changes: 4 additions & 0 deletions tests/DotLLM.Tests.Unit/Cpu/Kernels/DequantizeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ public void Q8_0_ScalarMatchesSimd_RandomBlocks()

Dequantize.DequantizeQ8_0Scalar(ptr, totalElements, scalarDest);

// Deliberately NOT a [SkippableFact]: the scalar and dispatch assertions below run
// on every machine, so gating only the AVX2 half keeps that coverage where AVX2 is absent.
if (Avx2.IsSupported)
{
Dequantize.DequantizeQ8_0Avx2(ptr, totalElements, simdDest);
Expand Down Expand Up @@ -304,6 +306,8 @@ public void Q5_0_ScalarVsAvx2_MatchOnPseudoRandomBlocks()

Dequantize.DequantizeQ5_0Scalar(ptr, totalElements, scalarDest);

// Deliberately NOT a [SkippableFact]: the scalar and dispatch assertions below run
// on every machine, so gating only the AVX2 half keeps that coverage where AVX2 is absent.
if (Avx2.IsSupported)
{
Dequantize.DequantizeQ5_0Avx2(ptr, totalElements, simdDest);
Expand Down
16 changes: 7 additions & 9 deletions tests/DotLLM.Tests.Unit/Cpu/Kernels/KvQuantizeTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.X86;
using DotLLM.Cpu.Kernels;
using Xunit;

Expand Down Expand Up @@ -37,11 +38,10 @@ public void Q4_0_Scalar_RoundTrip_WithinTolerance()
$"Max round-trip error {maxErr} exceeds expected {expectedMaxErr}");
}

[Fact]
[SkippableFact]
public void Q4_0_Avx2_MatchesScalar()
{
if (!System.Runtime.Intrinsics.X86.Avx2.IsSupported)
return; // Skip on non-AVX2 hardware
Skip.IfNot(Avx2.IsSupported, "Requires AVX2.");

float[] input = GenerateTestData(BlockSize * 8);
byte[] quantScalar = new byte[KvQuantize.Q4_0BlockBytes * 8];
Expand Down Expand Up @@ -77,11 +77,10 @@ public void Q4_0_ZeroInput_AllZeroOutput()
Assert.Equal(0f, output[i]);
}

[Fact]
[SkippableFact]
public void Q4_0_Dequant_Avx2_MatchesScalar()
{
if (!System.Runtime.Intrinsics.X86.Avx2.IsSupported)
return;
Skip.IfNot(Avx2.IsSupported, "Requires AVX2.");

float[] input = GenerateTestData(BlockSize * 4);
byte[] quantized = new byte[KvQuantize.Q4_0BlockBytes * 4];
Expand Down Expand Up @@ -135,11 +134,10 @@ public void Q8_0_RoundTrip_WithinTolerance()
$"Max round-trip error {maxErr} exceeds expected {expectedMaxErr}");
}

[Fact]
[SkippableFact]
public void Q8_0_Dequant_Avx2_MatchesScalar()
{
if (!System.Runtime.Intrinsics.X86.Avx2.IsSupported)
return;
Skip.IfNot(Avx2.IsSupported, "Requires AVX2.");

float[] input = GenerateTestData(BlockSize * 4);
byte[] quantized = new byte[KvQuantize.Q8_0BlockBytes * 4];
Expand Down
16 changes: 8 additions & 8 deletions tests/DotLLM.Tests.Unit/Cpu/Kernels/MatMulKQuantTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ public void QuantizeF32ToQ8_K_Scalar_RoundtripAccuracy()
}
}

[Fact]
[SkippableFact]
public void QuantizeF32ToQ8_K_Avx2MatchesScalar()
{
if (!Avx2.IsSupported) return;
Skip.IfNot(Avx2.IsSupported, "Requires AVX2.");

const int k = 512; // 2 blocks
var rng = new Random(42);
Expand Down Expand Up @@ -153,10 +153,10 @@ public void VecDotQ4_K_Q8_K_CrossVerifyAgainstDequant()
}
}

[Fact]
[SkippableFact]
public void VecDotQ4_K_Q8_K_ScalarMatchesAvx2()
{
if (!Avx2.IsSupported) return;
Skip.IfNot(Avx2.IsSupported, "Requires AVX2.");

const int superBlockCount = 4;
var rng = new Random(42);
Expand Down Expand Up @@ -216,10 +216,10 @@ public void VecDotQ6_K_Q8_K_CrossVerifyAgainstDequant()
}
}

[Fact]
[SkippableFact]
public void VecDotQ6_K_Q8_K_ScalarMatchesAvx2()
{
if (!Avx2.IsSupported) return;
Skip.IfNot(Avx2.IsSupported, "Requires AVX2.");

const int superBlockCount = 4;
var rng = new Random(42);
Expand Down Expand Up @@ -280,10 +280,10 @@ public void VecDotQ5_K_Q8_K_CrossVerifyAgainstDequant()
}
}

[Fact]
[SkippableFact]
public void VecDotQ5_K_Q8_K_ScalarMatchesAvx2()
{
if (!Avx2.IsSupported) return;
Skip.IfNot(Avx2.IsSupported, "Requires AVX2.");

const int superBlockCount = 4;
var rng = new Random(42);
Expand Down
4 changes: 2 additions & 2 deletions tests/DotLLM.Tests.Unit/Cpu/Kernels/MatMulQ5_0Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ public void VecDotQ5_0Q8_0Scalar_CrossVerifyAgainstDequant(int blockCount)

// ──────────────────── Q5_0 × Q8_0 scalar matches AVX2 ────────────────────

[Theory]
[SkippableTheory]
[InlineData(1)]
[InlineData(4)]
[InlineData(18)]
public void VecDotQ5_0Q8_0_ScalarMatchesAvx2(int blockCount)
{
if (!Avx2.IsSupported) return;
Skip.IfNot(Avx2.IsSupported, "Requires AVX2.");

var rng = new Random(42);
nint q5Ptr = AllocRandomQ5_0Blocks(blockCount, rng);
Expand Down
16 changes: 8 additions & 8 deletions tests/DotLLM.Tests.Unit/Cpu/Kernels/MatMulQ8_1Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,13 @@ public void QuantizeF32ToQ8_1_PrecomputedSumIsCorrect(int elementCount)

// ──────────────────── Q8_1 quantization: scalar matches AVX2 ────────────────────

[Theory]
[SkippableTheory]
[InlineData(32)]
[InlineData(576)]
[InlineData(1536)]
public void QuantizeF32ToQ8_1_ScalarMatchesAvx2(int elementCount)
{
if (!Avx2.IsSupported) return;
Skip.IfNot(Avx2.IsSupported, "Requires AVX2.");

var rng = new Random(42);
float* src = (float*)NativeMemory.AlignedAlloc((nuint)(elementCount * sizeof(float)), 64);
Expand Down Expand Up @@ -168,13 +168,13 @@ public void VecDotQ5_0Q8_1Scalar_MatchesQ8_0Path(int blockCount)

// ──────────────────── Q5_0 × Q8_1: scalar matches AVX2 ────────────────────

[Theory]
[SkippableTheory]
[InlineData(1)]
[InlineData(4)]
[InlineData(18)]
public void VecDotQ5_0Q8_1_ScalarMatchesAvx2(int blockCount)
{
if (!Avx2.IsSupported) return;
Skip.IfNot(Avx2.IsSupported, "Requires AVX2.");

var rng = new Random(42);
nint q5Ptr = AllocRandomQ5_0Blocks(blockCount, rng);
Expand All @@ -195,10 +195,10 @@ public void VecDotQ5_0Q8_1_ScalarMatchesAvx2(int blockCount)

// ──────────────────── Q5_0 × Q8_1: 4-row matches single row ────────────────────

[Fact]
[SkippableFact]
public void VecDotQ5_0Q8_1_4Row_MatchesSingleRow()
{
if (!Avx2.IsSupported) return;
Skip.IfNot(Avx2.IsSupported, "Requires AVX2.");

const int blockCount = 18;
var rng = new Random(42);
Expand Down Expand Up @@ -237,14 +237,14 @@ public void VecDotQ5_0Q8_1_4Row_MatchesSingleRow()

// ──────────────────── Q5_0 × Q8_1 AVX2: 2-block unroll odd/even block counts ────────────────────

[Theory]
[SkippableTheory]
[InlineData(1)] // odd, single-block tail only
[InlineData(3)] // odd, 1 unrolled pair + 1 tail
[InlineData(18)] // even, 9 unrolled pairs, no tail
[InlineData(19)] // odd, 9 unrolled pairs + 1 tail
public void VecDotQ5_0Q8_1_2BlockUnroll_OddAndEvenCounts(int blockCount)
{
if (!Avx2.IsSupported) return;
Skip.IfNot(Avx2.IsSupported, "Requires AVX2.");

var rng = new Random(42);
nint q5Ptr = AllocRandomQ5_0Blocks(blockCount, rng);
Expand Down
16 changes: 8 additions & 8 deletions tests/DotLLM.Tests.Unit/Cpu/Kernels/MatMulR4VnniTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,23 @@ public sealed unsafe class MatMulR4VnniTests

public static TheoryData<int> DiscriminatingBlockCounts() => [1, 2, 3, 8, 17, 18, 48, 128];

[Theory]
[SkippableTheory]
[MemberData(nameof(DiscriminatingBlockCounts))]
public void VecDotQ8_0Vnni_4RowsR4_MatchesScalarR4(int blockCount)
{
if (!VnniAvailable) return; // dispatch wiring is pinned by DispatchPrefersAvx2Tier
Skip.IfNot(VnniAvailable, "Requires AVX-512BW + AVX-VNNI."); // dispatch wiring is pinned by DispatchPrefersAvx2Tier

RunSingleGroup(blockCount, seed: 42, out float[] vnni, out float[] scalar, out _);

for (int r = 0; r < 4; r++)
AssertClose(scalar[r], vnni[r], blockCount, $"row {r}");
}

[Theory]
[SkippableTheory]
[MemberData(nameof(DiscriminatingBlockCounts))]
public void VecDotQ8_0Vnni_4RowsR4_MatchesAvx2R4(int blockCount)
{
if (!VnniAvailable) return; // dispatch wiring is pinned by DispatchPrefersAvx2Tier
Skip.IfNot(VnniAvailable, "Requires AVX-512BW + AVX-VNNI."); // dispatch wiring is pinned by DispatchPrefersAvx2Tier

RunSingleGroup(blockCount, seed: 7, out float[] vnni, out _, out float[] avx2);

Expand All @@ -59,11 +59,11 @@ public void VecDotQ8_0Vnni_4RowsR4_MatchesAvx2R4(int blockCount)
/// untouched — which also proves the kernel reads each row from its own interleaved offset
/// rather than accidentally aliasing one row four times.
/// </summary>
[Theory]
[SkippableTheory]
[MemberData(nameof(DiscriminatingBlockCounts))]
public void VecDotQ8_0Vnni_4RowsR4_DetectsPerturbedWeight(int blockCount)
{
if (!VnniAvailable) return; // dispatch wiring is pinned by DispatchPrefersAvx2Tier
Skip.IfNot(VnniAvailable, "Requires AVX-512BW + AVX-VNNI."); // dispatch wiring is pinned by DispatchPrefersAvx2Tier

const int perturbedRow = 2;
int k = blockCount * Q8_0GroupSize;
Expand Down Expand Up @@ -153,11 +153,11 @@ public void ComputeRowsQ8_0Interleaved_MatchesRowMajor(int m, int blockCount)
/// dispatch changed, which given the measurements would be a performance regression rather
/// than a correctness one, and would otherwise go unnoticed.
/// </summary>
[Theory]
[SkippableTheory]
[MemberData(nameof(DiscriminatingBlockCounts))]
public void ComputeRowsQ8_0Interleaved_DispatchPrefersAvx2Tier(int blockCount)
{
if (!Avx2.IsSupported) return;
Skip.IfNot(Avx2.IsSupported, "Requires AVX2.");

int k = blockCount * Q8_0GroupSize;

Expand Down
Loading