Skip to content

Commit b783fba

Browse files
committed
Update benchmarks
1 parent c2b4b64 commit b783fba

3 files changed

Lines changed: 67 additions & 245 deletions

File tree

Docs/BenchmarkResults.md

Lines changed: 9 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -1,133 +1,19 @@
11
# SlidingToggleButton Benchmark Results
22

3-
## Overview
3+
## Results (November 27, 2025)
44

5-
This document contains benchmark results for the SlidingToggleButton Swift package. Benchmarks are run using Swift Testing framework.
5+
| Metric | Value | Target |
6+
|--------|-------|--------|
7+
| First Render Time | 0.57ms | < 16.67ms (60fps) |
8+
| State Toggle Latency | 0.02ms | < 16.67ms (60fps) |
9+
| Memory Footprint | 152 bytes | < 512 bytes |
10+
| View Hierarchy Depth | 194 levels | < 250 levels |
11+
| Rapid Toggle (1000x) | 7.83ms | < 1000ms |
612

7-
## Test Environment
8-
9-
| Attribute | Value |
10-
|-----------|-------|
11-
| Platform | macOS 14+ / iOS 17+ |
12-
| Swift Version | 6.0 |
13-
| Test Framework | Swift Testing |
14-
15-
## Benchmark Categories
16-
17-
### 1. Initialization Benchmarks
18-
19-
#### Minimal Initialization (1000 iterations)
20-
Creates button instances with only required parameters.
21-
22-
| Metric | Target | Description |
23-
|--------|--------|-------------|
24-
| Time | < 1000ms | Total time for 1000 initializations |
25-
| Per Instance | < 1ms | Average time per initialization |
26-
27-
#### Full Custom Initialization (1000 iterations)
28-
Creates button instances with all custom parameters specified.
29-
30-
| Metric | Target | Description |
31-
|--------|--------|-------------|
32-
| Time | < 1000ms | Total time for 1000 initializations |
33-
| Per Instance | < 1ms | Average time per initialization |
34-
35-
### 2. Orientation Benchmarks
36-
37-
#### Horizontal vs Vertical Initialization (1000 iterations each)
38-
Compares initialization performance between horizontal and vertical orientations.
39-
40-
| Orientation | Target |
41-
|-------------|--------|
42-
| Horizontal | < 1000ms |
43-
| Vertical | < 1000ms |
44-
45-
### 3. State Management Benchmarks
46-
47-
#### Binding Updates (10000 iterations)
48-
Measures the performance of toggling the bound value.
49-
50-
| Metric | Target | Description |
51-
|--------|--------|-------------|
52-
| Time | < 500ms | Total time for 10000 toggles |
53-
| Per Toggle | < 0.05ms | Average time per toggle |
54-
55-
### 4. View Generation Benchmarks
56-
57-
#### Body Generation (100 iterations)
58-
Measures the time to generate the SwiftUI body.
59-
60-
| Metric | Target | Description |
61-
|--------|--------|-------------|
62-
| Time | < 1000ms | Total time for 100 body generations |
63-
| Per Generation | < 10ms | Average time per generation |
64-
65-
### 5. Memory Benchmarks
66-
67-
#### Multiple Instance Creation (1000 instances)
68-
Creates and holds 1000 button instances in memory.
69-
70-
| Metric | Target | Description |
71-
|--------|--------|-------------|
72-
| Time | < 1000ms | Total time to create all instances |
73-
| Instance Count | 1000 | Verify all instances created |
74-
75-
#### Defaults Access (100000 iterations)
76-
Measures the overhead of accessing static default values.
77-
78-
| Metric | Target | Description |
79-
|--------|--------|-------------|
80-
| Time | < 500ms | Total time for 100000 accesses |
81-
| Per Access | < 0.005ms | Average time per access |
13+
**Environment:** Apple Silicon (arm64e-apple-macos14.0), Swift 6.0
8214

8315
## Running Benchmarks
8416

85-
To run the benchmark tests:
86-
8717
```bash
88-
# Run all tests including benchmarks
89-
swift test
90-
91-
# Run only benchmark tests
9218
swift test --filter BenchmarkTests
93-
94-
# Run with verbose output to see timing results
95-
swift test --filter BenchmarkTests 2>&1 | grep -E "(Benchmark|ms)"
9619
```
97-
98-
## Results History
99-
100-
### Latest Results (Recorded: November 2024)
101-
102-
| Test | Result | Status |
103-
|------|--------|--------|
104-
| Minimal Initialization (1000x) | 1.80ms | PASS |
105-
| Full Custom Initialization (1000x) | 1.01ms | PASS |
106-
| Horizontal Initialization (1000x) | 1.84ms | PASS |
107-
| Vertical Initialization (1000x) | 0.65ms | PASS |
108-
| Binding Updates (10000x) | 1.33ms | PASS |
109-
| Body Generation (100x) | 2.50ms | PASS |
110-
| Multiple Instance Creation (1000x) | 1.76ms | PASS |
111-
| Defaults Access (100000x) | 8.70ms | PASS |
112-
113-
> **Note**: Run `swift test --filter BenchmarkTests` to update these results.
114-
> Results recorded on Apple Silicon (arm64e-apple-macos14.0)
115-
116-
## Performance Guidelines
117-
118-
### Acceptable Performance
119-
120-
- Initialization: < 1ms per instance
121-
- Binding updates: < 0.1ms per update
122-
- Body generation: < 10ms per generation
123-
- Defaults access: < 0.01ms per access
124-
125-
### Performance Considerations
126-
127-
1. **SwiftUI View Lifecycle**: The button uses `@State` for internal state management, which is handled efficiently by SwiftUI.
128-
129-
2. **Animation Performance**: Animations use `.spring(response: 0.3)` which is optimized for smooth 60fps rendering.
130-
131-
3. **Symbol Effects**: The `phaseAnimator` and `symbolEffect` modifiers leverage Metal rendering for efficient GPU-accelerated animations.
132-
133-
4. **Memory Footprint**: Each button instance has a minimal memory footprint with only essential stored properties.

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,16 @@ The following iOS 17+/macOS 14+ APIs are used:
9393
| Button Background Color | `.primary.opacity(0.4)` |
9494
| Icon Color | `.white` |
9595

96+
## Performance
97+
98+
| Metric | Value |
99+
|--------|-------|
100+
| First Render | 0.57ms |
101+
| State Toggle | 0.02ms |
102+
| Memory | 152 bytes |
103+
104+
Optimized for 60fps rendering. See [Benchmark Results](./Docs/BenchmarkResults.md) for details.
105+
96106
## License
97107

98108
**SlidingToggleButton** is available under the MIT license. See the [LICENSE](./LICENSE.md) for more info.

Tests/SlidingToggleButtonTests/BenchmarkTests.swift

Lines changed: 48 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -5,115 +5,80 @@ import Testing
55
@Suite("SlidingToggleButton Benchmark Tests")
66
struct SlidingToggleButtonBenchmarkTests {
77

8-
// MARK: - Initialization Benchmarks
9-
10-
@Test("Benchmark: Minimal Initialization (1000 iterations)")
11-
func benchmarkMinimalInitialization() {
8+
@Test("Benchmark: First Render Time")
9+
func benchmarkFirstRenderTime() {
1210
var value = false
1311
let binding = Binding(get: { value }, set: { value = $0 })
1412

1513
let startTime = CFAbsoluteTimeGetCurrent()
16-
for _ in 0..<1000 {
17-
_ = SlidingToggleButton(
18-
value: binding,
19-
startIconName: "sun.max.fill",
20-
endIconName: "moon.fill"
21-
)
22-
}
14+
let button = SlidingToggleButton(
15+
value: binding,
16+
startIconName: "sun.max.fill",
17+
endIconName: "moon.fill"
18+
)
19+
_ = button.body
2320
let endTime = CFAbsoluteTimeGetCurrent()
2421
let elapsed = (endTime - startTime) * 1000
2522

26-
print("Minimal Initialization (1000x): \(String(format: "%.2f", elapsed))ms")
27-
#expect(elapsed < 1000, "Initialization should complete in under 1 second")
23+
print("First Render Time: \(String(format: "%.3f", elapsed))ms")
24+
#expect(elapsed < 16.67, "First render should complete within one frame (16.67ms at 60fps)")
2825
}
2926

30-
@Test("Benchmark: Full Custom Initialization (1000 iterations)")
31-
func benchmarkFullCustomInitialization() {
27+
@Test("Benchmark: View Hierarchy Depth")
28+
func benchmarkViewHierarchyDepth() {
3229
var value = false
3330
let binding = Binding(get: { value }, set: { value = $0 })
3431

35-
let startTime = CFAbsoluteTimeGetCurrent()
36-
for _ in 0..<1000 {
37-
_ = SlidingToggleButton(
38-
value: binding,
39-
size: 48,
40-
padding: 16,
41-
backgroundColor: .red,
42-
buttonBackgroundColor: .blue,
43-
vertical: true,
44-
startIconName: "star.fill",
45-
endIconName: "heart.fill"
46-
)
47-
}
48-
let endTime = CFAbsoluteTimeGetCurrent()
49-
let elapsed = (endTime - startTime) * 1000
32+
let button = SlidingToggleButton(
33+
value: binding,
34+
startIconName: "sun.max.fill",
35+
endIconName: "moon.fill"
36+
)
5037

51-
print("Full Custom Initialization (1000x): \(String(format: "%.2f", elapsed))ms")
52-
#expect(elapsed < 1000, "Initialization should complete in under 1 second")
53-
}
38+
let bodyDescription = String(describing: button.body)
39+
let depth = bodyDescription.components(separatedBy: "(").count - 1
5440

55-
@Test("Benchmark: Horizontal vs Vertical Initialization")
56-
func benchmarkOrientationInitialization() {
57-
var value = false
58-
let binding = Binding(get: { value }, set: { value = $0 })
41+
print("View Hierarchy Depth: \(depth) levels")
42+
#expect(depth < 250, "View hierarchy should not be excessively deep")
43+
}
5944

60-
// Horizontal
61-
let horizontalStart = CFAbsoluteTimeGetCurrent()
62-
for _ in 0..<1000 {
63-
_ = SlidingToggleButton(
64-
value: binding,
65-
vertical: false,
66-
startIconName: "sun.max.fill",
67-
endIconName: "moon.fill"
68-
)
69-
}
70-
let horizontalEnd = CFAbsoluteTimeGetCurrent()
71-
let horizontalElapsed = (horizontalEnd - horizontalStart) * 1000
72-
73-
// Vertical
74-
let verticalStart = CFAbsoluteTimeGetCurrent()
75-
for _ in 0..<1000 {
76-
_ = SlidingToggleButton(
77-
value: binding,
78-
vertical: true,
79-
startIconName: "sun.max.fill",
80-
endIconName: "moon.fill"
81-
)
82-
}
83-
let verticalEnd = CFAbsoluteTimeGetCurrent()
84-
let verticalElapsed = (verticalEnd - verticalStart) * 1000
45+
@Test("Benchmark: Memory Footprint")
46+
func benchmarkMemoryFootprint() {
47+
let instanceSize = MemoryLayout<SlidingToggleButton>.size
48+
let instanceStride = MemoryLayout<SlidingToggleButton>.stride
49+
let instanceAlignment = MemoryLayout<SlidingToggleButton>.alignment
8550

86-
print("Horizontal Initialization (1000x): \(String(format: "%.2f", horizontalElapsed))ms")
87-
print("Vertical Initialization (1000x): \(String(format: "%.2f", verticalElapsed))ms")
51+
print("Instance Size: \(instanceSize) bytes")
52+
print("Instance Stride: \(instanceStride) bytes")
53+
print("Instance Alignment: \(instanceAlignment) bytes")
8854

89-
#expect(horizontalElapsed < 1000, "Horizontal initialization should complete in under 1 second")
90-
#expect(verticalElapsed < 1000, "Vertical initialization should complete in under 1 second")
55+
#expect(instanceSize < 512, "Instance should be under 512 bytes")
9156
}
9257

93-
@Test("Benchmark: State Toggle Binding Updates (10000 iterations)")
94-
func benchmarkBindingUpdates() {
58+
@Test("Benchmark: State Toggle Latency")
59+
func benchmarkStateToggleLatency() {
9560
var value = false
9661
let binding = Binding(get: { value }, set: { value = $0 })
9762

98-
_ = SlidingToggleButton(
63+
let button = SlidingToggleButton(
9964
value: binding,
10065
startIconName: "sun.max.fill",
10166
endIconName: "moon.fill"
10267
)
68+
_ = button.body
10369

10470
let startTime = CFAbsoluteTimeGetCurrent()
105-
for _ in 0..<10000 {
106-
value.toggle()
107-
}
71+
value = true
72+
_ = button.body
10873
let endTime = CFAbsoluteTimeGetCurrent()
10974
let elapsed = (endTime - startTime) * 1000
11075

111-
print("Binding Updates (10000x): \(String(format: "%.2f", elapsed))ms")
112-
#expect(elapsed < 500, "Binding updates should complete in under 500ms")
76+
print("State Toggle Latency: \(String(format: "%.3f", elapsed))ms")
77+
#expect(elapsed < 16.67, "State toggle should complete within one frame (16.67ms at 60fps)")
11378
}
11479

115-
@Test("Benchmark: Body Generation")
116-
func benchmarkBodyGeneration() {
80+
@Test("Benchmark: Rapid Toggle Stability")
81+
func benchmarkRapidToggleStability() {
11782
var value = false
11883
let binding = Binding(get: { value }, set: { value = $0 })
11984

@@ -123,57 +88,18 @@ struct SlidingToggleButtonBenchmarkTests {
12388
endIconName: "moon.fill"
12489
)
12590

91+
let iterations = 1000
12692
let startTime = CFAbsoluteTimeGetCurrent()
127-
for _ in 0..<100 {
93+
for _ in 0..<iterations {
94+
value.toggle()
12895
_ = button.body
12996
}
13097
let endTime = CFAbsoluteTimeGetCurrent()
13198
let elapsed = (endTime - startTime) * 1000
99+
let avgPerToggle = elapsed / Double(iterations)
132100

133-
print("Body Generation (100x): \(String(format: "%.2f", elapsed))ms")
134-
#expect(elapsed < 1000, "Body generation should complete in under 1 second")
135-
}
136-
137-
@Test("Benchmark: Memory - Multiple Instance Creation")
138-
func benchmarkMultipleInstances() {
139-
var value = false
140-
let binding = Binding(get: { value }, set: { value = $0 })
141-
142-
var buttons: [SlidingToggleButton] = []
143-
buttons.reserveCapacity(1000)
144-
145-
let startTime = CFAbsoluteTimeGetCurrent()
146-
for _ in 0..<1000 {
147-
let button = SlidingToggleButton(
148-
value: binding,
149-
startIconName: "sun.max.fill",
150-
endIconName: "moon.fill"
151-
)
152-
buttons.append(button)
153-
}
154-
let endTime = CFAbsoluteTimeGetCurrent()
155-
let elapsed = (endTime - startTime) * 1000
156-
157-
print("Multiple Instance Creation (1000x): \(String(format: "%.2f", elapsed))ms")
158-
print("Array count: \(buttons.count)")
159-
#expect(buttons.count == 1000)
160-
#expect(elapsed < 1000, "Multiple instance creation should complete in under 1 second")
161-
}
162-
163-
@Test("Benchmark: Defaults Access")
164-
func benchmarkDefaultsAccess() {
165-
let startTime = CFAbsoluteTimeGetCurrent()
166-
for _ in 0..<100000 {
167-
_ = SlidingToggleButtonDefaults.defaultSize
168-
_ = SlidingToggleButtonDefaults.padding
169-
_ = SlidingToggleButtonDefaults.backgroundColor
170-
_ = SlidingToggleButtonDefaults.buttonBackgroundColor
171-
_ = SlidingToggleButtonDefaults.vertical
172-
}
173-
let endTime = CFAbsoluteTimeGetCurrent()
174-
let elapsed = (endTime - startTime) * 1000
175-
176-
print("Defaults Access (100000x): \(String(format: "%.2f", elapsed))ms")
177-
#expect(elapsed < 500, "Defaults access should complete in under 500ms")
101+
print("Rapid Toggle (\(iterations)x): \(String(format: "%.2f", elapsed))ms total")
102+
print("Average per Toggle: \(String(format: "%.3f", avgPerToggle))ms")
103+
#expect(avgPerToggle < 1, "Each toggle cycle should be under 1ms")
178104
}
179105
}

0 commit comments

Comments
 (0)