Skip to content
Draft
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
24 changes: 24 additions & 0 deletions .github/workflows/performance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Performance guard

on:
pull_request:
branches: [ "main" ]
push:
branches: [ "main" ]
workflow_dispatch:

permissions:
contents: read

jobs:
release-benchmark:
name: Release 10k regression gate
runs-on: macos-latest
timeout-minutes: 20

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Run two-strike performance gate
run: bash script/ci_performance_gate.sh
11 changes: 8 additions & 3 deletions Sources/StorageScopeBenchmark/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ struct BenchmarkArguments {
var useSyntheticFixture = false
var keepFixture = false
var showFullPath = false
var streamingCallbacks = false
/// User-requested file count for the synthetic fixture. 0 means "use the v0.5.0 curated
/// 7-file default". >0 builds the scaled generator (`depth` directory levels deep,
/// `duplicateRatio` of items emitted as content-identical duplicates).
Expand All @@ -21,12 +22,13 @@ func usage() -> String {
Usage:
StorageScopeBenchmark [--show-full-path] <folder>
StorageScopeBenchmark --synthetic [--keep-fixture] [--show-full-path]
StorageScopeBenchmark --synthetic --items <n> [--depth <d>] [--duplicates <0..1>] [--keep-fixture]
StorageScopeBenchmark --synthetic --items <n> [--depth <d>] [--duplicates <0..1>] [--streaming] [--keep-fixture]

Scaled fixtures (--items, --depth, --duplicates) build a synthetic tree of N files
distributed across up to depth directory levels, with an optional fraction of
duplicates (pairs sharing identical content). Useful for capturing v0.5.x perf
baselines at 10k / 100k / 500k items.
Pass --streaming to install app-like progress and partial-snapshot callbacks.

Examples:
swift run StorageScopeBenchmark --synthetic --items 100000 --depth 8 --duplicates 0.2
Expand Down Expand Up @@ -55,6 +57,8 @@ func parseArguments(_ rawArguments: [String]) throws -> BenchmarkArguments {
arguments.keepFixture = true
case "--show-full-path":
arguments.showFullPath = true
case "--streaming":
arguments.streamingCallbacks = true
case "--items":
let raw = try consumeNextValue(flag: value)
guard let parsed = Int(raw), parsed >= 0 else {
Expand Down Expand Up @@ -142,7 +146,8 @@ do {

let report = try ScanBenchmarkRunner().run(
rootURL: rootURL,
showFullPath: arguments.showFullPath
showFullPath: arguments.showFullPath,
streamingCallbacks: arguments.streamingCallbacks
)
print(report.text)
} catch {
Expand All @@ -151,4 +156,4 @@ do {
Data("StorageScopeBenchmark failed: \(message)\n\n\(usage())\n".utf8)
)
exit(2)
}
}
9 changes: 9 additions & 0 deletions Sources/StorageScopeCore/Models/StorageScan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@ public struct StorageScan: Sendable {
public let duplicateCandidateItemLimit: Int
public let duplicateCandidateItemsRetained: Int
public let duplicateCandidateItemsConsidered: Int
public let duplicateCandidateEvictionCount: Int
public let duplicateCandidateLimitReached: Bool
public let snapshotBuildCount: Int
public let duplicateVerificationDuration: TimeInterval
public let duplicateVerificationBytesRead: Int64
public let enumerateDuration: TimeInterval
public let cleanupCandidates: [CleanupCandidate]
public let isPartial: Bool
Expand All @@ -51,8 +54,11 @@ public struct StorageScan: Sendable {
duplicateCandidateItemLimit: Int = 0,
duplicateCandidateItemsRetained: Int = 0,
duplicateCandidateItemsConsidered: Int = 0,
duplicateCandidateEvictionCount: Int = 0,
duplicateCandidateLimitReached: Bool = false,
snapshotBuildCount: Int = 0,
duplicateVerificationDuration: TimeInterval = 0,
duplicateVerificationBytesRead: Int64 = 0,
enumerateDuration: TimeInterval = 0,
cleanupCandidates: [CleanupCandidate],
isPartial: Bool = false
Expand All @@ -75,8 +81,11 @@ public struct StorageScan: Sendable {
self.duplicateCandidateItemLimit = duplicateCandidateItemLimit
self.duplicateCandidateItemsRetained = duplicateCandidateItemsRetained
self.duplicateCandidateItemsConsidered = duplicateCandidateItemsConsidered
self.duplicateCandidateEvictionCount = duplicateCandidateEvictionCount
self.duplicateCandidateLimitReached = duplicateCandidateLimitReached
self.snapshotBuildCount = snapshotBuildCount
self.duplicateVerificationDuration = duplicateVerificationDuration
self.duplicateVerificationBytesRead = duplicateVerificationBytesRead
self.enumerateDuration = enumerateDuration
self.cleanupCandidates = cleanupCandidates
self.isPartial = isPartial
Expand Down
Loading
Loading