Refactor code structure and optimize performance across multiple modules
This commit is contained in:
@@ -0,0 +1,302 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// BinaryLookupBenchmarks.cs
|
||||
// Sprint: SPRINT_20251226_014_BINIDX
|
||||
// Task: SCANINT-20 - Performance benchmarks for binary lookup
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using StellaOps.BinaryIndex.Core.Models;
|
||||
using StellaOps.BinaryIndex.Core.Services;
|
||||
|
||||
namespace StellaOps.Bench.BinaryLookup.Benchmarks;
|
||||
|
||||
/// <summary>
|
||||
/// Performance benchmarks for binary vulnerability lookup operations.
|
||||
/// Measures single and batch lookup performance, cache efficiency,
|
||||
/// and overall throughput for scanner integration.
|
||||
/// </summary>
|
||||
[MemoryDiagnoser]
|
||||
[SimpleJob(warmupCount: 3, iterationCount: 10)]
|
||||
public class BinaryLookupBenchmarks
|
||||
{
|
||||
private readonly BinaryIdentity[] _testIdentities = GenerateTestIdentities(100);
|
||||
private readonly byte[][] _testFingerprints = GenerateTestFingerprints(100);
|
||||
|
||||
[Params(1, 10, 50, 100)]
|
||||
public int BatchSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Generate sample binary identities for benchmarking.
|
||||
/// </summary>
|
||||
private static BinaryIdentity[] GenerateTestIdentities(int count)
|
||||
{
|
||||
return Enumerable.Range(0, count).Select(i => new BinaryIdentity
|
||||
{
|
||||
Format = BinaryFormat.Elf,
|
||||
BuildId = GenerateBuildId(i),
|
||||
FileSha256 = GenerateSha256(i),
|
||||
Architecture = "x86_64",
|
||||
BinaryKey = $"libtest{i}:1.0.{i}"
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate sample fingerprints for benchmarking.
|
||||
/// </summary>
|
||||
private static byte[][] GenerateTestFingerprints(int count)
|
||||
{
|
||||
return Enumerable.Range(0, count)
|
||||
.Select(i => GenerateFingerprintBytes(i))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private static string GenerateBuildId(int seed)
|
||||
{
|
||||
// Generate deterministic 40-char hex build ID
|
||||
var bytes = new byte[20];
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
bytes[i] = (byte)((seed + i * 17) % 256);
|
||||
}
|
||||
return Convert.ToHexString(bytes).ToLowerInvariant();
|
||||
}
|
||||
|
||||
private static string GenerateSha256(int seed)
|
||||
{
|
||||
var bytes = new byte[32];
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
bytes[i] = (byte)((seed + i * 31) % 256);
|
||||
}
|
||||
return "sha256:" + Convert.ToHexString(bytes).ToLowerInvariant();
|
||||
}
|
||||
|
||||
private static byte[] GenerateFingerprintBytes(int seed)
|
||||
{
|
||||
var bytes = new byte[64];
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
bytes[i] = (byte)((seed + i * 13) % 256);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark binary identity extraction from Build-ID.
|
||||
/// Target: < 1ms per identity
|
||||
/// </summary>
|
||||
[Benchmark(Description = "Identity extraction from Build-ID")]
|
||||
public BinaryIdentity BenchmarkIdentityExtraction()
|
||||
{
|
||||
return new BinaryIdentity
|
||||
{
|
||||
Format = BinaryFormat.Elf,
|
||||
BuildId = "8d8f09a0d7e2c1b3a5f4e6d8c0b2a4e6f8d0c2b4",
|
||||
FileSha256 = "sha256:abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
|
||||
Architecture = "x86_64",
|
||||
BinaryKey = "openssl:1.1.1w-1"
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark batch identity creation for scanner integration.
|
||||
/// Target: < 10ms per batch of 100
|
||||
/// </summary>
|
||||
[Benchmark(Description = "Batch identity creation")]
|
||||
public ImmutableArray<BinaryIdentity> BenchmarkBatchIdentityCreation()
|
||||
{
|
||||
return _testIdentities.Take(BatchSize)
|
||||
.ToImmutableArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark fingerprint hash computation.
|
||||
/// Target: < 5ms per fingerprint
|
||||
/// </summary>
|
||||
[Benchmark(Description = "Fingerprint hash computation")]
|
||||
public string BenchmarkFingerprintHash()
|
||||
{
|
||||
var fingerprint = _testFingerprints[0];
|
||||
using var sha256 = System.Security.Cryptography.SHA256.Create();
|
||||
var hash = sha256.ComputeHash(fingerprint);
|
||||
return Convert.ToHexString(hash);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark batch fingerprint comparison.
|
||||
/// Target: < 100ms for 100 comparisons
|
||||
/// </summary>
|
||||
[Benchmark(Description = "Batch fingerprint comparison")]
|
||||
public int BenchmarkBatchFingerprintComparison()
|
||||
{
|
||||
var target = _testFingerprints[0];
|
||||
var matches = 0;
|
||||
|
||||
for (int i = 0; i < BatchSize; i++)
|
||||
{
|
||||
var candidate = _testFingerprints[i];
|
||||
var similarity = ComputeHammingSimilarity(target, candidate);
|
||||
if (similarity > 0.7)
|
||||
{
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark binary key generation for lookup.
|
||||
/// Target: < 0.1ms per key
|
||||
/// </summary>
|
||||
[Benchmark(Description = "Binary key generation")]
|
||||
public string BenchmarkBinaryKeyGeneration()
|
||||
{
|
||||
var identity = _testIdentities[0];
|
||||
return $"{identity.BinaryKey}:{identity.Architecture}:{identity.Format}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark lookup key construction with distro.
|
||||
/// Target: < 0.1ms per key
|
||||
/// </summary>
|
||||
[Benchmark(Description = "Distro-aware lookup key")]
|
||||
public string BenchmarkDistroLookupKey()
|
||||
{
|
||||
var identity = _testIdentities[0];
|
||||
var distro = "debian";
|
||||
var release = "bookworm";
|
||||
return $"{distro}:{release}:{identity.BinaryKey}";
|
||||
}
|
||||
|
||||
private static double ComputeHammingSimilarity(byte[] a, byte[] b)
|
||||
{
|
||||
if (a.Length != b.Length) return 0.0;
|
||||
|
||||
var matching = 0;
|
||||
var total = a.Length * 8;
|
||||
|
||||
for (int i = 0; i < a.Length; i++)
|
||||
{
|
||||
var xor = (byte)(a[i] ^ b[i]);
|
||||
matching += 8 - PopCount(xor);
|
||||
}
|
||||
|
||||
return (double)matching / total;
|
||||
}
|
||||
|
||||
private static int PopCount(byte b)
|
||||
{
|
||||
var count = 0;
|
||||
while (b != 0)
|
||||
{
|
||||
count += b & 1;
|
||||
b >>= 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmarks for cache layer performance.
|
||||
/// </summary>
|
||||
[MemoryDiagnoser]
|
||||
[SimpleJob(warmupCount: 3, iterationCount: 10)]
|
||||
public class CacheLayerBenchmarks
|
||||
{
|
||||
private readonly Dictionary<string, object> _mockCache = new();
|
||||
private readonly string[] _testKeys;
|
||||
|
||||
public CacheLayerBenchmarks()
|
||||
{
|
||||
// Pre-populate cache with test data
|
||||
_testKeys = Enumerable.Range(0, 1000)
|
||||
.Select(i => $"binary:tenant1:buildid{i:D8}")
|
||||
.ToArray();
|
||||
|
||||
foreach (var key in _testKeys.Take(800)) // 80% pre-cached
|
||||
{
|
||||
_mockCache[key] = new { Matches = Array.Empty<object>() };
|
||||
}
|
||||
}
|
||||
|
||||
[Params(10, 100, 500)]
|
||||
public int LookupCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark cache hit performance.
|
||||
/// Target: > 80% hit rate, < 0.01ms per hit
|
||||
/// </summary>
|
||||
[Benchmark(Description = "Cache hit lookup")]
|
||||
public int BenchmarkCacheHits()
|
||||
{
|
||||
var hits = 0;
|
||||
for (int i = 0; i < LookupCount; i++)
|
||||
{
|
||||
var key = _testKeys[i % 800]; // Always hit
|
||||
if (_mockCache.ContainsKey(key))
|
||||
{
|
||||
hits++;
|
||||
}
|
||||
}
|
||||
return hits;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark cache miss handling.
|
||||
/// Target: < 1ms per miss for key generation
|
||||
/// </summary>
|
||||
[Benchmark(Description = "Cache miss handling")]
|
||||
public int BenchmarkCacheMisses()
|
||||
{
|
||||
var misses = 0;
|
||||
for (int i = 0; i < LookupCount; i++)
|
||||
{
|
||||
var key = _testKeys[800 + (i % 200)]; // Always miss
|
||||
if (!_mockCache.ContainsKey(key))
|
||||
{
|
||||
misses++;
|
||||
}
|
||||
}
|
||||
return misses;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark mixed workload (realistic scenario).
|
||||
/// Target: > 80% hit rate overall
|
||||
/// </summary>
|
||||
[Benchmark(Description = "Mixed cache workload")]
|
||||
public (int hits, int misses) BenchmarkMixedWorkload()
|
||||
{
|
||||
var hits = 0;
|
||||
var misses = 0;
|
||||
var random = new Random(42); // Deterministic seed
|
||||
|
||||
for (int i = 0; i < LookupCount; i++)
|
||||
{
|
||||
var key = _testKeys[random.Next(_testKeys.Length)];
|
||||
if (_mockCache.ContainsKey(key))
|
||||
{
|
||||
hits++;
|
||||
}
|
||||
else
|
||||
{
|
||||
misses++;
|
||||
}
|
||||
}
|
||||
|
||||
return (hits, misses);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benchmark cache key generation.
|
||||
/// </summary>
|
||||
[Benchmark(Description = "Cache key generation")]
|
||||
public string BenchmarkCacheKeyGeneration()
|
||||
{
|
||||
var tenant = "tenant1";
|
||||
var buildId = "8d8f09a0d7e2c1b3a5f4e6d8c0b2a4e6f8d0c2b4";
|
||||
return $"binary:{tenant}:{buildId}";
|
||||
}
|
||||
}
|
||||
20
src/__Tests/__Benchmarks/binary-lookup/Program.cs
Normal file
20
src/__Tests/__Benchmarks/binary-lookup/Program.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// Program.cs
|
||||
// Sprint: SPRINT_20251226_014_BINIDX
|
||||
// Task: SCANINT-20 - Performance benchmarks for binary lookup
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using BenchmarkDotNet.Running;
|
||||
|
||||
namespace StellaOps.Bench.BinaryLookup;
|
||||
|
||||
/// <summary>
|
||||
/// Entry point for binary lookup benchmark suite.
|
||||
/// </summary>
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var summary = BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
|
||||
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.14.0" Condition="'$(OS)' == 'Windows_NT'" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\BinaryIndex\__Libraries\StellaOps.BinaryIndex.Core\StellaOps.BinaryIndex.Core.csproj" />
|
||||
<ProjectReference Include="..\..\..\BinaryIndex\__Libraries\StellaOps.BinaryIndex.Fingerprints\StellaOps.BinaryIndex.Fingerprints.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user