Performance
Benchmarks and performance characteristics
Benchmark Results
All benchmarks run on Bun, measuring operations per second:
| Operation | Vex | Zod | Valibot | vs Zod | vs Valibot |
|---|---|---|---|---|---|
| string | 367M | 50M | 50M | 7.4x | 7.3x |
| 128M | 12M | 17M | 11x | 7.5x | |
| url | 110M | 4M | 5M | 28x | 23x |
| uuid | 117M | 16M | 6M | 7.3x | 19x |
| object (3 fields) | 18M | 5M | 7M | 3.9x | 2.7x |
| object (nested) | 11M | 5M | 5M | 2x | 2.1x |
| array[50] | 8.5M | 780K | 1.4M | 11x | 6x |
| safeParse (valid) | 15M | 7M | 6M | 2.1x | 2.5x |
| safeParse (invalid) | 44M | 376K | 2.6M | 118x | 17x |
Average: 12x faster than Zod, 6x faster than Valibot
Why is Vex Fast?
1. Pure Functional Design
Validators are constants, not factory functions:
// Vex - zero allocation
str // constant
str(email) // compose constants
// Others - allocates on every call
z.string().email() // creates new objects
v.string() // creates new objects2. No Wrapper Objects
Direct function composition without intermediate objects:
// Vex internals (simplified)
const str = (value) => {
if (typeof value !== 'string') throw error
return value
}
// Others create class instances
class StringSchema {
email() { return new EmailSchema(this) }
// ...
}3. Error Path Optimization
Error paths are only computed when validation fails:
// Only allocate on error, not success path
if (!valid) {
throw new ValidationError(message, path)
}Running Benchmarks
# Clone the repo
git clone https://github.com/SylphxAI/vex.git
cd vex
# Install dependencies
bun install
# Run benchmarks
bun run benchTree-shaking
Vex is fully tree-shakeable:
// Only imports what you use
import { str, num, object } from '@sylphx/vex'
// Bundle size: ~2KB (minified + gzipped)Compare to Zod which imports the entire library regardless of usage.