Claude Code Plugins

Community-maintained marketplace

Feedback

performance-analyzer

@mikha08-rgb/VoiceLite
2
0

Analyze and optimize VoiceLite performance including RAM usage, CPU utilization, transcription latency, and memory leaks. Activates when performance targets are violated or optimization is needed.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name performance-analyzer
description Analyze and optimize VoiceLite performance including RAM usage, CPU utilization, transcription latency, and memory leaks. Activates when performance targets are violated or optimization is needed.

Performance Analyzer

Monitor and optimize VoiceLite performance against strict targets.

When This Skill Activates

  • Keywords: "slow", "memory leak", "high CPU", "performance", "optimization"
  • Metrics mentioned: RAM, CPU, latency, FPS, responsiveness
  • Violations: ">100MB idle", ">300MB active", ">5% CPU idle", "slow transcription"
  • After changes to: AudioRecorder, PersistentWhisperService, MainWindow
  • User reports: "App is slow", "High memory usage", "Laggy UI"

VoiceLite Performance Targets

From CLAUDE.md - these are STRICT requirements:

Metric Target Failure Threshold How to Measure
Idle RAM <100MB >150MB Task Manager or dotMemory after 30s idle
Active RAM <300MB >400MB During transcription (peak usage)
Idle CPU <5% >10% Task Manager (30s average)
Transcription Latency <200ms >500ms Time from speech stop to text injection
Whisper Processing (tiny) <0.8s >2s 5-second audio sample
Whisper Processing (small) <3s >8s 5-second audio sample
Startup Time <2s >5s From launch to tray icon visible

All targets are non-negotiable - VoiceLite must be lightweight and responsive.

Quick Performance Check

Manual Task Manager Method

# 1. Launch VoiceLite.exe

# 2. Wait 30 seconds (reach idle state)

# 3. Open Task Manager (Ctrl+Shift+Esc)
#    - Details tab → Find VoiceLite.exe
#    - Check "Memory (Private Working Set)"
#      ✓ Should be <100MB
#      ✗ If >150MB → Memory leak investigation needed

# 4. Check CPU usage over 30 seconds
#    ✓ Should average <5%
#    ✗ If >10% → CPU profiling needed

# 5. Test transcription
#    - Record 5 seconds of audio
#    - Check memory spike
#      ✓ Should stay <300MB
#      ✗ If >400MB → Audio buffer leak

# 6. Check whisper process
#    - After recording, check whisper.exe appears briefly
#    - Should disappear after <2s for tiny model
#    ✗ If lingers → Zombie process (disposal issue)

Automated Performance Tests

// VoiceLite.Tests/Performance/PerformanceTests.cs

[Fact]
public async Task TranscriptionLatency_ShouldBeLessThan200ms()
{
    // Arrange
    var recorder = new AudioRecorder();
    var whisper = new PersistentWhisperService();

    // Act
    var stopwatch = Stopwatch.StartNew();
    var audio = await recorder.Record5Seconds();
    var text = await whisper.TranscribeAsync(audio);
    stopwatch.Stop();

    // Assert
    stopwatch.ElapsedMilliseconds.Should().BeLessThan(200);
}

[Fact]
public void IdleMemory_ShouldBeLessThan100MB()
{
    // Arrange
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();

    // Act
    var memoryMB = GC.GetTotalMemory(false) / 1024 / 1024;

    // Assert
    memoryMB.Should().BeLessThan(100);
}

[Fact]
public async Task WhisperProcessing_TinyModel_ShouldBeLessThan800ms()
{
    // Arrange
    var service = new PersistentWhisperService("ggml-tiny.bin");
    var test5sAudio = "test_audio_5s.wav";

    // Act
    var stopwatch = Stopwatch.StartNew();
    await service.TranscribeAsync(test5sAudio);
    stopwatch.Stop();

    // Assert
    stopwatch.ElapsedMilliseconds.Should().BeLessThan(800);
}

Memory Profiling with dotMemory

Installation

# Download JetBrains dotMemory (free 30-day trial)
# Or use dotMemory CLI for CI/CD

Profiling Workflow

# 1. Attach to running VoiceLite process
# Tools → Attach to Process → Select VoiceLite.exe

# 2. Take baseline snapshot (idle state)
# Wait 30s, click "Get Snapshot"

# 3. Perform 10 transcriptions

# 4. Take second snapshot

# 5. Compare snapshots
# Look for growing:
# - byte[] arrays (audio buffers not disposed)
# - Process objects (whisper zombies)
# - Event handlers (subscription leaks)

Red Flags in dotMemory

Growing byte[] arrays:

Snapshot 1: 1.5 MB byte[]
Snapshot 2: 15.2 MB byte[]

→ Audio buffers not being released
→ Check AudioRecorder.Dispose() called
→ Verify _audioBuffer cleared after use

Growing Process objects:

Snapshot 1: 0 Process instances
Snapshot 2: 10 Process instances

→ Whisper.exe processes not disposed
→ Check PersistentWhisperService.TranscribeAsync finally block
→ Ensure process?.Dispose() called

Growing event subscriptions:

Snapshot 1: 5 event handlers
Snapshot 2: 50 event handlers

→ Events subscribed but never unsubscribed
→ Check -= in Dispose methods
→ Example: _audioRecorder.AudioFileReady -= OnAudioFileReady

CPU Profiling with PerfView

Collect CPU Trace

# 1. Download PerfView.exe (free, Microsoft)
# https://github.com/microsoft/perfview/releases

# 2. Run as Administrator

# 3. Collect → Collect
# or command line:
PerfView.exe collect -ThreadTime

# 4. Perform actions in VoiceLite (transcriptions)

# 5. Stop collection

# 6. Analyze → CPU Stacks

Analyzing CPU Hotspots

Look for:

  • High % in specific method → Optimization target
  • Tight loops → Potential infinite loop or inefficiency
  • Excessive GC → Too many allocations
  • Blocking I/O on UI thread → Move to background

Example findings:

Method: AudioRecorder.ProcessBuffer - 45% CPU
→ Buffer processing inefficient
→ Optimization: Use Span<byte> instead of byte[]

Method: GC.Collect - 15% CPU
→ Too many allocations
→ Optimization: Reuse buffers, reduce new object creation

Common Performance Issues

Issue 1: Memory Leak in AudioRecorder

Symptom: RAM grows from 50MB → 200MB+ after 20 recordings

Diagnosis:

// Check if Dispose is called
public class AudioRecorder : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("AudioRecorder.Dispose called"); // Add logging
        _waveIn?.Dispose();
        _waveIn = null;
    }
}

// Check if actually disposed after use
using (var recorder = new AudioRecorder())
{
    await recorder.RecordAsync();
} // Should call Dispose here

Fix: Ensure using statement or explicit Dispose() call

Issue 2: Whisper Zombie Processes

Symptom: Multiple whisper.exe processes remain after transcription

Diagnosis:

# Check running whisper processes
tasklist | findstr whisper.exe

# Should be 0 when idle
# If >0 → Disposal issue

Fix:

// PersistentWhisperService.cs
public async Task<string> TranscribeAsync(string path)
{
    Process? process = null;
    try
    {
        process = new Process { /* ... */ };
        process.Start();
        // ...
    }
    finally
    {
        // CRITICAL: Always dispose
        process?.Kill(); // If still running
        process?.Dispose();
    }
}

Issue 3: UI Thread Blocking

Symptom: UI freezes during transcription

Diagnosis:

// Check if long-running work on UI thread
private void OnRecordButtonClick(object sender, RoutedEventArgs e)
{
    // ❌ BAD: Blocks UI thread
    var result = _whisperService.TranscribeAsync(audio).Result;

    // ✅ GOOD: Async/await keeps UI responsive
    var result = await _whisperService.TranscribeAsync(audio);
}

Fix: Use async/await throughout, never .Result or .Wait()

Issue 4: Large Audio Buffer Allocations

Symptom: Memory spikes during recording

Optimization:

// Before: New allocation every buffer
private void OnDataAvailable(object sender, WaveInEventArgs e)
{
    var buffer = new byte[e.BytesRecorded]; // Allocation!
    Buffer.BlockCopy(e.Buffer, 0, buffer, 0, e.BytesRecorded);
}

// After: Reuse buffer
private readonly byte[] _reusableBuffer = new byte[8192];
private void OnDataAvailable(object sender, WaveInEventArgs e)
{
    Buffer.BlockCopy(e.Buffer, 0, _reusableBuffer, 0, e.BytesRecorded);
}

Whisper Performance Optimization

Command-Line Flags (v1.0.85-88 Optimizations)

# Current optimized command (67-73% faster)
whisper.exe \
  -m ggml-tiny.bin \
  -f audio.wav \
  --no-timestamps \
  --language en \
  --beam-size 1 \        # Greedy decoding (fastest)
  --best-of 1 \          # Single pass
  --entropy-thold 3.0 \  # Early stopping
  --no-fallback \        # No secondary attempts
  --max-context 64 \     # Smaller context window
  --flash-attn           # Flash attention (12% faster)

# v1.0.84 command (baseline)
whisper.exe -m ggml-tiny.bin -f audio.wav --no-timestamps --language en
# ~2.5s for 5s audio

# v1.0.88 command (current)
# ~0.7s for 5s audio (67% faster!)

Model Performance Comparison

Model Size (Q8_0) Accuracy Processing Time (5s audio)
ggml-tiny.bin 42MB 80-85% 0.7s ✅
ggml-base.bin 78MB 85-90% 1.5s ✅
ggml-small.bin 253MB 90-93% 3s ✅
ggml-medium.bin 1.5GB 95-97% 12s ⚠️
ggml-large-v3.bin 1.6GB 97-98% 8s ⚠️

Performance Regression Testing

CI/CD Performance Tests

# Run performance tests in CI
dotnet test --filter "Category=Performance"

# Set environment variable for thresholds
PERFORMANCE_STRICT=true dotnet test

# Fail build if performance regresses

Benchmarking Script

# benchmark.ps1
$iterations = 10
$results = @()

for ($i = 0; $i -lt $iterations; $i++) {
    $sw = [Diagnostics.Stopwatch]::StartNew()

    # Test transcription
    ./VoiceLite/bin/Release/VoiceLite.exe --test-transcribe test_5s.wav

    $sw.Stop()
    $results += $sw.ElapsedMilliseconds
}

$avg = ($results | Measure-Object -Average).Average
Write-Host "Average transcription time: $avg ms"

if ($avg -gt 800) {
    Write-Error "Performance regression! Target: <800ms, Actual: $avg ms"
    exit 1
}

Optimization Checklist

Before releasing new version:

  • Run performance tests: dotnet test --filter Performance
  • Check idle RAM: Task Manager <100MB
  • Check active RAM: Peak <300MB during transcription
  • Profile with dotMemory: No growing byte[] or Process objects
  • Check whisper zombies: tasklist | findstr whisper = 0 when idle
  • Test transcription latency: <800ms for tiny model
  • Verify CPU usage: <5% idle, <30% during transcription
  • No UI blocking: Async/await throughout
  • Disposal tests pass: All IDisposable objects properly cleaned up

Tools Summary

Tool Purpose When to Use
Task Manager Quick RAM/CPU check Every build, quick validation
dotMemory Memory leak detection After major changes, before release
PerfView CPU profiling When CPU >10%, optimization needed
Performance Tests Automated regression detection CI/CD, every commit
Benchmark Script Transcription speed validation After whisper command changes

Historical Performance Improvements

v1.0.84 → v1.0.88 (67-73% faster):

  • Added --entropy-thold, --no-fallback, optimal threads (40% faster)
  • Upgraded whisper.cpp v1.6.0 → v1.7.6 (20-40% faster)
  • Added --flash-attn (7-12% faster)
  • Q8_0 quantization (same speed, 45% smaller)

Target: Maintain sub-800ms transcription for tiny model