# web-worker-string-benchmark
**Repository Path**: mlight-lee/web-worker-string-benchmark
## Basic Information
- **Project Name**: web-worker-string-benchmark
- **Description**: Web Worker String Transfer Benchmark
- **Primary Language**: TypeScript
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2025-11-08
- **Last Updated**: 2025-11-08
## Categories & Tags
**Categories**: Uncategorized
**Tags**: benchmark, webworker, demo, TypeScript
## README
# Web Worker String Transfer Benchmark
This benchmark compares two methods for passing large strings to web workers:
1. **Direct String Transfer**: Passing strings directly via `postMessage()`
2. **ArrayBuffer Transfer**: Converting strings to ArrayBuffer, transferring, then decoding
## 🚀 Quick Start
```bash
# Install dependencies
pnpm install
# Build the project
pnpm build
# Run the benchmark
pnpm preview
```
Then open http://localhost:5173/ in your browser.
## 📊 What This Benchmark Tests
### Method 1: Direct String Transfer
- Uses `postMessage(string)` to send data directly
- Browser uses structured clone algorithm
- String is copied (not transferred) to worker
- No encoding/decoding overhead
### Method 2: ArrayBuffer Transfer
- Converts string to ArrayBuffer using `TextEncoder`
- Uses `postMessage(buffer, [buffer])` for zero-copy transfer
- Decodes ArrayBuffer back to string using `TextDecoder`
- Includes encoding/decoding overhead
## 🎯 Test Parameters
- **String Sizes**: 1MB, 5MB, 10MB, 50MB, 100MB, 200MB
- **String Types**:
- Random ASCII characters
- Unicode (UTF-8) characters
- Repeated patterns
- **Iterations**: 1-20 runs per method (configurable)
- **Metrics**: Average, Min, Max, Median times
## 📈 Test Results
The benchmark measures which method is more efficient across different **string sizes** (1 MB → 100 MB), **string types** (ASCII vs Unicode), and multiple **iterations** in Google Chrome version 139.
- **String Sizes Tested**: 1 MB, 5 MB, 10 MB, 50 MB, 100 MB
- **String Types**: Random ASCII, Unicode (UTF-8 encoded)
- **Iterations per Test**: 20
- **Metrics Collected**: Average, Minimum, Maximum, Median execution times
Test results summary is as follows.
### ASCII Strings
| Size | Direct Transfer (Avg) | ArrayBuffer Transfer (Avg) | Faster Method | Relative Gain |
|------|------------------------|-----------------------------|---------------|---------------|
| 1 MB | 3.90 ms | 9.42 ms | Direct | **58.6%** |
| 5 MB | 13.70 ms | 39.83 ms | Direct | **65.6%** |
| 10 MB| 22.40 ms | 66.58 ms | Direct | **66.3%** |
| 50 MB| 93.11 ms | 338.79 ms | Direct | **72.5%** |
|100 MB| 211.33 ms | 692.54 ms | Direct | **69.5%** |
**Observation:**
- Direct transfer consistently outperforms ArrayBuffer transfer.
- Overhead from `TextEncoder`/`TextDecoder` dominates, despite zero-copy transfer.
- Performance gap grows with size, reaching **~70% faster at 50–100 MB**.
### Unicode Strings
| Size | Direct Transfer (Avg) | ArrayBuffer Transfer (Avg) | Faster Method | Relative Gain |
|------|------------------------|-----------------------------|---------------|---------------|
| 1 MB | 2.92 ms | 16.57 ms | Direct | **82.3%** |
| 5 MB | 11.99 ms | 80.48 ms | Direct | **85.1%** |
| 10 MB| 26.67 ms | 166.73 ms | Direct | **84.0%** |
| 50 MB| 93.90 ms | 700.85 ms | Direct | **86.6%** |
|100 MB| 202.35 ms | 1529.89 ms | Direct | **86.8%** |
**Observation:**
- For Unicode, the performance difference is even larger than ASCII.
- Encoding/decoding multibyte characters greatly increases ArrayBuffer overhead.
- Direct transfer is **up to 7–8× faster** at large sizes (50–100 MB).
## Test Results Analysis
### Analysis
1. **Encoding/Decoding Cost Dominates**
- `TextEncoder` and `TextDecoder` introduce significant overhead, particularly for Unicode strings.
- The supposed advantage of zero-copy transfer with ArrayBuffers is outweighed by this cost.
2. **Scalability with String Size**
- Direct transfer scales better as string size grows.
- Although both methods slow down at larger sizes, Direct Transfer consistently maintains lower execution times and smaller variance.
3. **Stability**
- Direct Transfer shows wider min/max variation (due to structured clone performance variability across runs).
- ArrayBuffer results are more stable but consistently slower.
4. **Unicode vs ASCII**
- Direct Transfer performs similarly for ASCII and Unicode (encoding handled by browser internally).
- ArrayBuffer suffers much more with Unicode because encoding produces larger byte arrays and decoding is expensive.
### Conclusion
- **Winner:** 🚀 **Direct String Transfer via `postMessage(string)`**
- **Key Finding:** Direct transfer is consistently faster, simpler, and scales better with larger string sizes.
- **Performance Gap:**
- **ASCII:** ~60–70% faster
- **Unicode:** ~80–87% faster
### Practical Recommendations
- Use **Direct String Transfer** for large strings in most cases.
- ArrayBuffer transfer is only useful when:
- Data is already in binary form (e.g., images, typed arrays).
- Zero-copy semantics are critical.
- You must avoid multiple string copies across contexts.
- For string workloads, **avoid unnecessary encoding/decoding overhead**.
## 🔬 Technical Details
### Timing Methodology
- Measures end-to-end time from `postMessage()` to worker response
- Includes all encoding/decoding overhead
- Multiple iterations to account for variance
- Uses `performance.now()` for high precision
### Worker Processing
Do nothing to avoid it affects testing results. You can edit `src/worker.ts` to simulate your actual use case.
### Memory Considerations
- Direct transfer: 2x memory usage (original + copy)
- ArrayBuffer transfer: 3x memory usage (original + encoded + decoded)
## 🛠️ Customization
### Adding New Test Sizes
Edit `index.html` and add options to the `testSize` select:
```html
```
### Adding New String Types
Modify the `generateTestString()` function:
```javascript
case 'binary':
for (let i = 0; i < bytes; i++) {
str += String.fromCharCode(Math.floor(Math.random() * 256));
}
break;
```
### Modifying Worker Processing
Edit `src/worker.ts` to simulate your actual use case:
```typescript
function processStringData(data: string): boolean {
// Add your specific string processing logic here
return true;
}
```
For now, function `doNothing` is called in worker.
## 📋 Browser Compatibility
- **Chrome/Edge**: Full support
- **Firefox**: Full support
- **Safari**: Full support
- **Mobile browsers**: May have different performance characteristics
## 🐛 Troubleshooting
### Build Issues
```bash
# Clear node_modules and reinstall
rm -rf node_modules package-lock.json
pnpm install
```
### Worker Not Loading
- Check browser console for errors
- Ensure `dist/assets/worker-********.js` exists after build
- Verify CORS settings if serving from different domain
### Performance Anomalies
- Close other browser tabs to reduce memory pressure
- Run multiple iterations to get consistent results
- Test on different browsers for comparison
## 📚 Further Reading
- [MDN: Using Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers)
- [MDN: Transferable Objects](https://developer.mozilla.org/en-US/docs/Web/API/Transferable)
- [V8 Blog: ArrayBuffer Performance](https://v8.dev/blog/array-buffer)
- [Web Performance: Memory Management](https://web.dev/memory-management/)
## 🤝 Contributing
Feel free to submit issues or pull requests to improve the benchmark:
- Add new test scenarios
- Improve timing accuracy
- Add more detailed metrics
- Support for different data types