Skip to content

Commit 525d40a

Browse files
authored
Merge pull request #7 from Alokzh/docs-improvement
Updated blogpost with OrderedCollection disadvantages & Added variance analysis to benchmark
2 parents 5ab1c2b + 402d2f0 commit 525d40a

5 files changed

Lines changed: 68 additions & 39 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,10 @@ log := CTFIFOBuffer withCapacity: 1000.
120120
| Operation | OrderedCollection | Array | CTFIFOBuffer |
121121
|-----------|------------------|--------|-------------|
122122
| Add item | O(1) | O(1) | O(1) |
123-
| Remove old | O(n) | O(n) | O(1) |
123+
| Remove old | O(n) | O(1) | O(1) |
124124
| Memory usage | Unlimited growth | Fixed/reallocated | Fixed |
125125

126+
**Note**: OrderedCollection can cause memory spikes - when capacity is exceeded, it creates a new array 2x the size and copies all elements over, leading to O(n) performance degradation. In contrast, CTFIFOBuffer maintains constant O(1) performance with fixed memory usage.
126127

127128
## Contributing
128129

docs/Circular_Buffer.md

Lines changed: 66 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -153,51 +153,79 @@ previousPage := browserHistory pop.
153153

154154
I conducted comprehensive performance tests comparing three approaches i.e Arrays, Circular Buffers & Ordered Collections for maintaining the last 100 items. Here's what the data reveals:
155155

156-
### Test Run 1: Individual Performance
156+
### Test Run: Average Performance
157157

158-
![](Performance-Single-Run.png)
159-
160-
The first screenshot shows a single test run where I measured the execution time for each approach. You can see in the transcript:
161-
162-
- **Array**: 66ms
163-
- **Buffer**: 140ms
164-
- **OrderedCollection**: 299ms
158+
```smalltalk
159+
| totalOperations capacity arrayResults bufferResults orderedCollectionResults arrayMean bufferMean orderedMean arrayVariance bufferVariance orderedVariance |
160+
161+
totalOperations := 1000000.
162+
capacity := 100.
163+
164+
arrayResults := OrderedCollection new.
165+
bufferResults := OrderedCollection new.
166+
orderedCollectionResults := OrderedCollection new.
167+
168+
Transcript show: 'Average result after running each benchmark for 100 times...'; cr.
169+
170+
100 timesRepeat: [
171+
arrayResults add: ([
172+
| array currentIndex |
173+
array := Array new: capacity.
174+
currentIndex := 1.
175+
1 to: totalOperations do: [ :i |
176+
array at: currentIndex put: i.
177+
currentIndex := currentIndex \\ capacity + 1.
178+
].
179+
] timeToRun asMilliSeconds).
180+
181+
bufferResults add: ([
182+
| buffer |
183+
buffer := CTFIFOBuffer withCapacity: capacity.
184+
1 to: totalOperations do: [ :i |
185+
buffer push: i.
186+
].
187+
] timeToRun asMilliSeconds).
188+
189+
orderedCollectionResults add: ([
190+
| collection |
191+
collection := OrderedCollection new.
192+
1 to: totalOperations do: [ :i |
193+
collection add: i.
194+
collection size > capacity ifTrue: [
195+
collection removeFirst.
196+
].
197+
].
198+
] timeToRun asMilliSeconds).
199+
].
165200
166-
This shows the relative performance - OrderedCollection is clearly the slowest due to all those expensive removeFirst operations.
167-
### Test Run 2: Average Performance
201+
"Calculate averages and variance"
202+
arrayMean := arrayResults sum / arrayResults size.
203+
bufferMean := bufferResults sum / bufferResults size.
204+
orderedMean := orderedCollectionResults sum / orderedCollectionResults size.
168205
169-
![](Performance-Average.png)
206+
arrayVariance := (arrayResults collect: [ :each | (each - arrayMean) squared ]) sum / arrayResults size.
207+
bufferVariance := (bufferResults collect: [ :each | (each - bufferMean) squared ]) sum / bufferResults size.
208+
orderedVariance := (orderedCollectionResults collect: [ :each | (each - orderedMean) squared ]) sum / orderedCollectionResults size.
170209
171-
The second screenshot shows the average execution time over 100 test runs. The results are as follows:
210+
"Display results"
211+
Transcript show: 'Array: ', arrayMean rounded asString, ' ms (variance: ', (arrayVariance roundTo: 0.01) asString, ')'; cr.
212+
Transcript show: 'Buffer: ', bufferMean rounded asString, ' ms (variance: ', (bufferVariance roundTo: 0.01) asString, ')'; cr.
213+
Transcript show: 'OrderedCollection: ', orderedMean rounded asString, ' ms (variance: ', (orderedVariance roundTo: 0.01) asString, ')'; cr.
214+
```
172215

173-
- **Array**: ~6 ms
174-
- **Buffer**: ~14 ms
175-
- **OrderedCollection**: ~31 ms
216+
### Results (average execution time over 100 test runs):
217+
- **Array**: ~6 ms (variance: low)
218+
- **Buffer**: ~14 ms (variance: low)
219+
- **OrderedCollection**: ~31 ms (variance: high)
176220

177-
These averages confirm the trends observed in the individual test run - Circular Buffers provide a solid middle ground between raw speed and ease of use, while Ordered Collections lag behind due to their inherent inefficiencies.
221+
### Why These Results Matter
178222

179-
### Test Run 3: Performance Benchmark
180-
![](Performance-Bench.png)
181-
Using Pharo's bench method to measure sustained performance, measured in operations per 5 seconds. The results are:
223+
**Array** is fastest but requires complex manual index management and wraparound logic - easy to introduce bugs.
182224

183-
- **Array**: ~756 iterations/5sec
184-
- **Buffer**: ~351 iterations/5sec
185-
- **OrderedCollection**: ~170 iterations/5sec
225+
**Buffer** delivers excellent performance while eliminating all complexity. It's built on arrays internally, so it inherits array access costs plus method call overhead, making it ~2.3x slower than raw arrays. However, this trade-off is worth it because you get 43% of array speed with zero complexity.
186226

187-
### Why These Results Matter
227+
**OrderedCollection** has multiple serious problems:
228+
- **Slow removeFirst operations**: Shifts hundreds of elements every time
229+
- **Memory spikes**: When internal capacity is exceeded, it creates a new array double the size and copies all elements, causing temporary memory usage spikes
188230

189-
- **Array (Manual Management)**
190-
- Fastest performance
191-
- Requires manual index logic and careful boundary checks
192-
- Easy to introduce bugs & Hard to maintain
193-
194-
- **Circular Buffer**
195-
- Nearly as fast as arrays
196-
- Delivers 43% of Array speed while eliminating 100% of the complexity
197-
- Automatically manages size
198-
- Clean, safe, and maintainable for most use cases
199-
200-
- **OrderedCollection**
201-
- Slowest performance
202-
- Gets destroyed by `removeFirst` operations that shift hundreds of elements every time
203-
- Not suitable for high-volume data management
231+
This makes OrderedCollection unsuitable for high-volume streaming data or real-time applications.

docs/Performance-Average.png

-203 KB
Binary file not shown.

docs/Performance-Bench.png

-162 KB
Binary file not shown.

docs/Performance-Single-Run.png

-151 KB
Binary file not shown.

0 commit comments

Comments
 (0)