mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-09-23 15:34:34 +00:00
Add WriteBenchmarks, BaselineComparisonBenchmarks, and comprehensive documentation
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using SharpCompress.Archives;
|
||||
|
||||
namespace SharpCompress.Performance;
|
||||
|
||||
/// <summary>
|
||||
/// Benchmarks comparing current code against a baseline.
|
||||
/// Use [Baseline] attribute to mark the reference benchmark.
|
||||
/// </summary>
|
||||
[MemoryDiagnoser]
|
||||
[RankColumn]
|
||||
public class BaselineComparisonBenchmarks : BenchmarkBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Baseline benchmark for Zip archive reading.
|
||||
/// This serves as the reference point for comparison.
|
||||
/// </summary>
|
||||
[Benchmark(Baseline = true)]
|
||||
public void ZipArchiveRead_Baseline()
|
||||
{
|
||||
var path = GetTestArchivePath("Zip.deflate.zip");
|
||||
using var archive = ArchiveFactory.Open(path);
|
||||
foreach (var entry in archive.Entries.Where(e => !e.IsDirectory))
|
||||
{
|
||||
using var stream = entry.OpenEntryStream();
|
||||
stream.CopyTo(Stream.Null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current implementation benchmark for Zip archive reading.
|
||||
/// BenchmarkDotNet will compare this against the baseline.
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public void ZipArchiveRead_Current()
|
||||
{
|
||||
var path = GetTestArchivePath("Zip.deflate.zip");
|
||||
using var archive = ArchiveFactory.Open(path);
|
||||
foreach (var entry in archive.Entries.Where(e => !e.IsDirectory))
|
||||
{
|
||||
using var stream = entry.OpenEntryStream();
|
||||
stream.CopyTo(Stream.Null);
|
||||
}
|
||||
}
|
||||
}
|
||||
131
tests/SharpCompress.Performance/README.md
Normal file
131
tests/SharpCompress.Performance/README.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# SharpCompress Performance Benchmarks
|
||||
|
||||
This project uses [BenchmarkDotNet](https://benchmarkdotnet.org/) to measure and track performance of SharpCompress archive operations.
|
||||
|
||||
## Running Benchmarks
|
||||
|
||||
### Run All Benchmarks
|
||||
```bash
|
||||
cd tests/SharpCompress.Performance
|
||||
dotnet run -c Release
|
||||
```
|
||||
|
||||
### Run Specific Benchmark Classes
|
||||
```bash
|
||||
# Run only Archive API benchmarks
|
||||
dotnet run -c Release -- --filter "*ArchiveReadBenchmarks*"
|
||||
|
||||
# Run only Reader API benchmarks
|
||||
dotnet run -c Release -- --filter "*ReaderBenchmarks*"
|
||||
```
|
||||
|
||||
### Run Specific Benchmark Methods
|
||||
```bash
|
||||
# Run only Zip benchmarks
|
||||
dotnet run -c Release -- --filter "*Zip*"
|
||||
|
||||
# Run a specific method
|
||||
dotnet run -c Release -- --filter "ArchiveReadBenchmarks.ZipArchiveRead"
|
||||
```
|
||||
|
||||
### Quick Dry Run (for testing)
|
||||
```bash
|
||||
dotnet run -c Release -- --job dry
|
||||
```
|
||||
|
||||
## Benchmark Categories
|
||||
|
||||
### ArchiveReadBenchmarks
|
||||
Tests the **Archive API** which provides random access to entries with seekable streams. Covers:
|
||||
- Zip (deflate compression)
|
||||
- Tar (uncompressed)
|
||||
- Tar.gz (gzip compression)
|
||||
- Tar.bz2 (bzip2 compression)
|
||||
- 7Zip (LZMA2 compression)
|
||||
- Rar
|
||||
|
||||
### ReaderBenchmarks
|
||||
Tests the **Reader API** which provides forward-only streaming for non-seekable streams. Covers:
|
||||
- Zip
|
||||
- Tar
|
||||
- Tar.gz
|
||||
- Tar.bz2
|
||||
- Rar
|
||||
|
||||
### WriteBenchmarks
|
||||
Tests the **Writer API** for creating archives using forward-only writing. Covers:
|
||||
- Zip (deflate compression)
|
||||
- Tar (uncompressed)
|
||||
- Tar.gz (gzip compression)
|
||||
|
||||
### BaselineComparisonBenchmarks
|
||||
Example benchmark showing how to compare implementations using the `[Baseline]` attribute. The baseline benchmark serves as a reference point, and BenchmarkDotNet calculates the ratio of performance between baseline and other methods.
|
||||
|
||||
## Comparing Against Previous Versions
|
||||
|
||||
### Using Baseline Attribute
|
||||
Mark one benchmark with `[Baseline = true]` and BenchmarkDotNet will show relative performance:
|
||||
|
||||
```csharp
|
||||
[Benchmark(Baseline = true)]
|
||||
public void MethodA() { /* ... */ }
|
||||
|
||||
[Benchmark]
|
||||
public void MethodB() { /* ... */ }
|
||||
```
|
||||
|
||||
Results will show ratios like "1.5x slower" or "0.8x faster" compared to the baseline.
|
||||
|
||||
### Using BenchmarkDotNet.Artifacts for Historical Comparison
|
||||
BenchmarkDotNet saves results to `BenchmarkDotNet.Artifacts/results/`. You can:
|
||||
|
||||
1. Run benchmarks and save the results
|
||||
2. Keep a snapshot of the results file
|
||||
3. Compare new runs against saved results
|
||||
|
||||
### Using Different NuGet Versions (Advanced)
|
||||
To compare against a published NuGet package:
|
||||
|
||||
1. Create a separate benchmark project referencing the NuGet package
|
||||
2. Use BenchmarkDotNet's `[SimpleJob]` attribute with different runtimes
|
||||
3. Reference both the local project and NuGet package in different jobs
|
||||
|
||||
## Interpreting Results
|
||||
|
||||
BenchmarkDotNet provides:
|
||||
- **Mean**: Average execution time
|
||||
- **Error**: Half of 99.9% confidence interval
|
||||
- **StdDev**: Standard deviation of measurements
|
||||
- **Allocated**: Memory allocated per operation
|
||||
- **Rank**: Relative ranking (when using `[RankColumn]`)
|
||||
- **Ratio**: Relative performance vs baseline (when using `[Baseline]`)
|
||||
|
||||
## Output Artifacts
|
||||
|
||||
Results are saved to `BenchmarkDotNet.Artifacts/results/`:
|
||||
- `*.csv`: Raw data for further analysis
|
||||
- `*-report.html`: HTML report with charts
|
||||
- `*-report-github.md`: Markdown report for GitHub
|
||||
- `*.log`: Detailed execution log
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always run in Release mode**: Debug builds have significant overhead
|
||||
2. **Close other applications**: Minimize system noise during benchmarks
|
||||
3. **Run multiple times**: Look for consistency across runs
|
||||
4. **Use appropriate workload**: Ensure benchmarks run for at least 100ms
|
||||
5. **Track trends**: Compare results over time to detect regressions
|
||||
6. **Archive results**: Keep snapshots of benchmark results for historical comparison
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
Consider adding benchmarks to CI/CD to:
|
||||
- Detect performance regressions automatically
|
||||
- Track performance trends over time
|
||||
- Compare PR performance against main branch
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [BenchmarkDotNet Documentation](https://benchmarkdotnet.org/articles/overview.html)
|
||||
- [BenchmarkDotNet Configuration](https://benchmarkdotnet.org/articles/configs/configs.html)
|
||||
- [BenchmarkDotNet Baseline](https://benchmarkdotnet.org/articles/features/baselines.html)
|
||||
105
tests/SharpCompress.Performance/WriteBenchmarks.cs
Normal file
105
tests/SharpCompress.Performance/WriteBenchmarks.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Writers;
|
||||
|
||||
namespace SharpCompress.Performance;
|
||||
|
||||
/// <summary>
|
||||
/// Benchmarks for Writer operations.
|
||||
/// Tests creating archives with different compression formats using forward-only Writer API.
|
||||
/// </summary>
|
||||
[MemoryDiagnoser]
|
||||
public class WriteBenchmarks : BenchmarkBase
|
||||
{
|
||||
private string _tempOutputPath = null!;
|
||||
private readonly string[] _testFiles = null!;
|
||||
|
||||
public WriteBenchmarks()
|
||||
{
|
||||
// Get some test files to compress
|
||||
var originalPath = Path.Combine(
|
||||
Path.GetDirectoryName(TEST_ARCHIVES_PATH)!,
|
||||
"Original"
|
||||
);
|
||||
if (Directory.Exists(originalPath))
|
||||
{
|
||||
_testFiles = Directory.GetFiles(originalPath).Take(5).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
_tempOutputPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
Directory.CreateDirectory(_tempOutputPath);
|
||||
}
|
||||
|
||||
[GlobalCleanup]
|
||||
public void Cleanup()
|
||||
{
|
||||
if (Directory.Exists(_tempOutputPath))
|
||||
{
|
||||
Directory.Delete(_tempOutputPath, true);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void ZipWriterWrite()
|
||||
{
|
||||
if (_testFiles == null || _testFiles.Length == 0)
|
||||
return;
|
||||
|
||||
var outputFile = Path.Combine(_tempOutputPath, "test.zip");
|
||||
using var stream = File.Create(outputFile);
|
||||
using var writer = WriterFactory.Open(
|
||||
stream,
|
||||
ArchiveType.Zip,
|
||||
new WriterOptions(CompressionType.Deflate)
|
||||
);
|
||||
foreach (var file in _testFiles)
|
||||
{
|
||||
writer.Write(Path.GetFileName(file), file);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TarWriterWrite()
|
||||
{
|
||||
if (_testFiles == null || _testFiles.Length == 0)
|
||||
return;
|
||||
|
||||
var outputFile = Path.Combine(_tempOutputPath, "test.tar");
|
||||
using var stream = File.Create(outputFile);
|
||||
using var writer = WriterFactory.Open(
|
||||
stream,
|
||||
ArchiveType.Tar,
|
||||
new WriterOptions(CompressionType.None)
|
||||
);
|
||||
foreach (var file in _testFiles)
|
||||
{
|
||||
writer.Write(Path.GetFileName(file), file);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TarGzWriterWrite()
|
||||
{
|
||||
if (_testFiles == null || _testFiles.Length == 0)
|
||||
return;
|
||||
|
||||
var outputFile = Path.Combine(_tempOutputPath, "test.tar.gz");
|
||||
using var stream = File.Create(outputFile);
|
||||
using var writer = WriterFactory.Open(
|
||||
stream,
|
||||
ArchiveType.Tar,
|
||||
new WriterOptions(CompressionType.GZip)
|
||||
);
|
||||
foreach (var file in _testFiles)
|
||||
{
|
||||
writer.Write(Path.GetFileName(file), file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user