Skip to content
Merged
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
10 changes: 9 additions & 1 deletion .claude/settings.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,15 @@
"Bash(git log:*)",
"Bash(git rm:*)",
"Bash(git checkout:*)",
"Bash(ffplay:*)"
"Bash(ffplay:*)",
"WebFetch(domain:humanfactors.arc.nasa.gov)",
"WebFetch(domain:developer.nvidia.com)",
"WebFetch(domain:bartwronski.com)",
"WebFetch(domain:surma.dev)",
"Bash(\"E:\\\\source\\\\vectorascii\\\\ConsoleImage\\\\test-publish\\\\consoleimage.exe\" tools --verify)",
"Bash(git stash:*)",
"WebFetch(domain:news.ycombinator.com)",
"Bash(git status:*)"
]
},
"enableAllProjectMcpServers": true,
Expand Down
6 changes: 3 additions & 3 deletions ConsoleImage.Benchmarks/BrailleRendererBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace ConsoleImage.Benchmarks;
/// Run with: dotnet run -c Release -- --filter *Braille*
/// </summary>
[MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.Net90)]
[SimpleJob]
public class BrailleRendererBenchmarks
{
private Image<Rgba32> _largeImage = null!;
Expand Down Expand Up @@ -130,7 +130,7 @@ public string RenderSmall_NoColor()
/// Benchmarks for brightness calculation optimizations.
/// </summary>
[MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.Net90)]
[SimpleJob]
public class BrightnessCalculationBenchmarks
{
private float[] _largeBuffer = null!;
Expand Down Expand Up @@ -226,7 +226,7 @@ private static (float min, float max) GetMinMaxUnrolled(float[] buffer)
/// Benchmarks for ANSI escape sequence generation.
/// </summary>
[MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.Net90)]
[SimpleJob]
public class AnsiEscapeBenchmarks
{
private static readonly string[] GreyscaleEscapes = InitGreyscale();
Expand Down
200 changes: 200 additions & 0 deletions ConsoleImage.Benchmarks/ShapeMatchingBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
using BenchmarkDotNet.Attributes;
using ConsoleImage.Core;

namespace ConsoleImage.Benchmarks;

/// <summary>
/// Benchmarks for BrailleCharacterMap shape vector matching.
/// Run with: dotnet run -c Release -- --filter *ShapeMatching*
/// </summary>
[MemoryDiagnoser]
[SimpleJob]
public class BrailleShapeMatchingBenchmarks
{
private BrailleCharacterMap _brailleMap = null!;
private float[][] _randomVectors = null!;
private float[][] _sparseVectors = null!;
private float[][] _denseVectors = null!;

[GlobalSetup]
public void Setup()
{
_brailleMap = new BrailleCharacterMap();

var random = new Random(42);

// Random 8D vectors (covers full range)
_randomVectors = new float[1000][];
for (var i = 0; i < _randomVectors.Length; i++)
{
_randomVectors[i] = new float[8];
for (var j = 0; j < 8; j++)
_randomVectors[i][j] = (float)random.NextDouble();
}

// Sparse vectors (mostly 0, few dots on - like thin lines)
_sparseVectors = new float[1000][];
for (var i = 0; i < _sparseVectors.Length; i++)
{
_sparseVectors[i] = new float[8];
for (var j = 0; j < 8; j++)
_sparseVectors[i][j] = random.NextDouble() < 0.25 ? (float)random.NextDouble() : 0f;
}

// Dense vectors (mostly 1, few dots off - like filled areas)
_denseVectors = new float[1000][];
for (var i = 0; i < _denseVectors.Length; i++)
{
_denseVectors[i] = new float[8];
for (var j = 0; j < 8; j++)
_denseVectors[i][j] = random.NextDouble() < 0.25 ? (float)random.NextDouble() : 1f;
}
}

[Benchmark(Baseline = true)]
public char FindBestMatch_Random_Cached()
{
char result = ' ';
for (var i = 0; i < _randomVectors.Length; i++)
result = _brailleMap.FindBestMatch(_randomVectors[i]);
return result;
}

[Benchmark]
public char FindBestMatch_Random_BruteForce()
{
char result = ' ';
for (var i = 0; i < _randomVectors.Length; i++)
result = _brailleMap.FindBestMatchBruteForce(_randomVectors[i]);
return result;
}

[Benchmark]
public char FindBestMatch_Sparse()
{
char result = ' ';
for (var i = 0; i < _sparseVectors.Length; i++)
result = _brailleMap.FindBestMatch(_sparseVectors[i]);
return result;
}

[Benchmark]
public char FindBestMatch_Dense()
{
char result = ' ';
for (var i = 0; i < _denseVectors.Length; i++)
result = _brailleMap.FindBestMatch(_denseVectors[i]);
return result;
}
}

/// <summary>
/// Benchmarks for ASCII CharacterMap with different character set sizes.
/// Measures the impact of the expanded full-printable character set.
/// Run with: dotnet run -c Release -- --filter *CharacterSet*
/// </summary>
[MemoryDiagnoser]
[SimpleJob]
public class CharacterSetBenchmarks
{
private CharacterMap _classicMap = null!; // 70 chars (old default)
private CharacterMap _fullMap = null!; // 95 chars (new default)
private CharacterMap _extendedMap = null!; // 93 chars
private ShapeVector[] _testVectors = null!;

[GlobalSetup]
public void Setup()
{
_classicMap = new CharacterMap(CharacterMap.ClassicCharacterSet);
_fullMap = new CharacterMap(CharacterMap.DefaultCharacterSet);
_extendedMap = new CharacterMap(CharacterMap.ExtendedCharacterSet);

var random = new Random(42);
_testVectors = new ShapeVector[1000];
for (var i = 0; i < _testVectors.Length; i++)
_testVectors[i] = new ShapeVector(
(float)random.NextDouble(),
(float)random.NextDouble(),
(float)random.NextDouble(),
(float)random.NextDouble(),
(float)random.NextDouble(),
(float)random.NextDouble()
);
}

[Benchmark(Baseline = true)]
public char Classic_70Chars()
{
char result = ' ';
for (var i = 0; i < _testVectors.Length; i++)
result = _classicMap.FindBestMatch(_testVectors[i]);
return result;
}

[Benchmark]
public char Full_95Chars()
{
char result = ' ';
for (var i = 0; i < _testVectors.Length; i++)
result = _fullMap.FindBestMatch(_testVectors[i]);
return result;
}

[Benchmark]
public char Extended_93Chars()
{
char result = ' ';
for (var i = 0; i < _testVectors.Length; i++)
result = _extendedMap.FindBestMatch(_testVectors[i]);
return result;
}

[Benchmark]
public char Classic_BruteForce()
{
char result = ' ';
for (var i = 0; i < _testVectors.Length; i++)
result = _classicMap.FindBestMatchBruteForce(_testVectors[i]);
return result;
}

[Benchmark]
public char Full_BruteForce()
{
char result = ' ';
for (var i = 0; i < _testVectors.Length; i++)
result = _fullMap.FindBestMatchBruteForce(_testVectors[i]);
return result;
}
}

/// <summary>
/// Benchmarks for disk cache performance (CharacterMap startup).
/// Run with: dotnet run -c Release -- --filter *DiskCache*
/// </summary>
[MemoryDiagnoser]
[SimpleJob]
[IterationCount(5)]
[WarmupCount(2)]
public class DiskCacheBenchmarks
{
[Benchmark(Baseline = true)]
public CharacterMap CreateMap_Classic_CacheHit()
{
// Second creation should hit disk cache
return new CharacterMap(CharacterMap.ClassicCharacterSet);
}

[Benchmark]
public CharacterMap CreateMap_Full_CacheHit()
{
return new CharacterMap(CharacterMap.DefaultCharacterSet);
}

[Benchmark]
public BrailleCharacterMap CreateBrailleMap()
{
// Mathematical generation - no disk cache needed
return new BrailleCharacterMap();
}
}
Loading
Loading