Benchmark Results
Generated: 2026-08-08 17:07:54 UTC
If you're interested in diving deeper, check out our benchmark repository.
Library Notes
Feature Comparison
| Feature | Pedantigo | Playground | Ozzo | Huma | Godantic | Godasse |
|---|---|---|---|---|---|---|
| Declarative constraints | ✅ tags | ✅ tags | ✅ rules | ✅ tags | ✅ methods | ❌ hand-written |
| JSON Schema generation | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ |
| Default values | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ |
| Unmarshal + validate | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ |
| Validate existing struct | ✅ | ✅ | ✅ | ❌ | ✅ | ❌* |
*Godasse requires hand-written Validate() methods
Library Descriptions
-
Pedantigo - Struct tag-based validation (
validate:"required,email,min=5"). JSON Schema generation with caching. -
Playground (go-playground/validator) - Struct tag-based validation. Rich constraint library, no JSON Schema.
-
Ozzo (ozzo-validation) - Rule builder API (
validation.Field(&u.Name, validation.Required, validation.Length(2,100))). No struct tags. -
Huma - OpenAPI-focused. Validates
map[string]anyagainst schemas, not structs directly. -
Godantic - Method-based constraints (
FieldName() FieldOptions[T]). JSON Schema, defaults, streaming partial JSON. -
Godasse - Deserializer with
default:tag. All constraint validation requires hand-writtenValidate()methods.
Getting the Best Performance
New[T]() does the expensive work once: it walks the struct via reflection, resolves every constraint tag, and builds an internal field-constraint cache (plus the JSON field deserializers). That one-time cost is what the New section below measures (microsecond range). Every other operation - Validate, Unmarshal, Marshal, Schema - reuses that precomputed cache and runs in the hundreds-of-ns to low-µs range, which is why those numbers consistently beat libraries that re-resolve constraints or re-walk structs on every call.
This only pays off if the *Validator[T] returned by New is built once and reused - not recreated per request. Two ways to do that:
Module-level variable (sufficient to call the validator directly):
var userValidator = validator.New[User]()
func handleCreateUser(body []byte) (*User, error) {
return userValidator.Unmarshal(body) // reuses the cached field constraints
}
Register (needed in addition, only if a framework integration - e.g. the Echo Binder plugin, or UnmarshalInto - must find the validator for a type it only knows via reflect.Type at runtime, not through your module-level variable):
var _ = validator.Register(validator.New[User]())
Register[T] may be called exactly once per type - a second call for the same type panics, by design. A type could have multiple differently-configured validators (different Options), and pedantigo has no way to guess which one a framework plugin should resolve to, so it refuses to silently pick one. Call Register from exactly one package-level var declaration per type.
Validate
Validate existing struct (no JSON parsing)
| Struct | Pedantigo | Playground | Ozzo | Huma | Godantic | Godasse |
|---|---|---|---|---|---|---|
| Simple | 1.01 µs (10 allocs) | 1.65 µs (7 allocs) | 9.20 µs (43 allocs) | unsupported | 4.26 µs (48 allocs) | unsupported |
| Complex | 1.62 µs (15 allocs) | 2.69 µs (9 allocs) | 8.87 µs (139 allocs) | unsupported | 10.47 µs (120 allocs) | unsupported |
| Large | 1.12 µs (22 allocs) | 1.33 µs (3 allocs) | 33.63 µs (254 allocs) | unsupported | 10.48 µs (126 allocs) | unsupported |
JSONValidate
JSON bytes → struct, then a separate validate step
| Struct | Pedantigo | Playground | Ozzo | Huma | Godantic | Godasse |
|---|---|---|---|---|---|---|
| Simple | 2.51 µs (19 allocs) | 3.16 µs (16 allocs) | unsupported | 2.59 µs (26 allocs) | unsupported | unsupported |
| Complex | 6.84 µs (39 allocs) | 8.06 µs (33 allocs) | unsupported | 7.65 µs (78 allocs) | unsupported | unsupported |
Marshal
Validate + JSON marshal
| Struct | Pedantigo | Playground | Ozzo | Huma | Godantic | Godasse |
|---|---|---|---|---|---|---|
| Simple | 1.36 µs (11 allocs) | 2.18 µs (9 allocs) | unsupported | unsupported | unsupported | unsupported |
Unmarshal
JSON bytes → validated struct in a single call
| Struct | Pedantigo | Playground | Ozzo | Huma | Godantic | Godasse |
|---|---|---|---|---|---|---|
| Simple | 3.70 µs (39 allocs) | unsupported | unsupported | unsupported | 8.46 µs (81 allocs) | 3.46 µs (42 allocs) |
| Complex | 11.26 µs (122 allocs) | unsupported | unsupported | unsupported | 35.96 µs (285 allocs) | 12.25 µs (149 allocs) |
New
Validator creation overhead
| Struct | Pedantigo | Playground | Ozzo | Huma | Godantic | Godasse |
|---|---|---|---|---|---|---|
| Simple | 11.74 µs (129 allocs) | 12.22 µs (187 allocs) | unsupported | 23.31 µs (255 allocs) | 20.19 µs (305 allocs) | 4.98 µs (72 allocs) |
| Complex | 27.12 µs (299 allocs) | unsupported | unsupported | 57.99 µs (515 allocs) | 5.57 µs (75 allocs) | 17.30 µs (243 allocs) |
Schema
JSON Schema generation
| Struct | Pedantigo | Playground | Ozzo | Huma | Godantic | Godasse |
|---|---|---|---|---|---|---|
| Uncached | 20.01 µs (227 allocs) | unsupported | unsupported | 23.37 µs (255 allocs) | unsupported | unsupported |
| Cached | 15 ns (0 allocs) | unsupported | unsupported | 466 ns (6 allocs) | unsupported | unsupported |
OpenAPI
OpenAPI-compatible schema generation
| Struct | Pedantigo | Playground | Ozzo | Huma | Godantic | Godasse |
|---|---|---|---|---|---|---|
| Uncached | 20.65 µs (229 allocs) | unsupported | unsupported | 23.35 µs (255 allocs) | unsupported | unsupported |
| Cached | 15 ns (0 allocs) | unsupported | unsupported | 461 ns (6 allocs) | unsupported | unsupported |
Summary
Validate_Simple (struct validation)
| Library | ns/op | allocs | vs Pedantigo |
|---|---|---|---|
| Pedantigo | 1.01 µs | 10 | baseline |
| Playground | 1.65 µs | 7 | 1.62x slower |
| Ozzo | 9.20 µs | 43 | 9.08x slower |
| Huma | - | - | - |
| Godantic | 4.26 µs | 48 | 4.21x slower |
| Godasse | - | - | - |
Validate_Complex (nested structs)
| Library | ns/op | allocs | vs Pedantigo |
|---|---|---|---|
| Pedantigo | 1.62 µs | 15 | baseline |
| Playground | 2.69 µs | 9 | 1.66x slower |
| Ozzo | 8.87 µs | 139 | 5.46x slower |
| Huma | - | - | - |
| Godantic | 10.47 µs | 120 | 6.45x slower |
| Godasse | - | - | - |
JSONValidate_Simple (JSON → struct, then validate)
| Library | ns/op | allocs | vs Pedantigo |
|---|---|---|---|
| Pedantigo | 2.51 µs | 19 | baseline |
| Playground | 3.16 µs | 16 | 1.26x slower |
| Ozzo | - | - | - |
| Huma | 2.59 µs | 26 | 1.03x slower |
| Godantic | - | - | - |
| Godasse | - | - | - |
JSONValidate_Complex (nested JSON)
| Library | ns/op | allocs | vs Pedantigo |
|---|---|---|---|
| Pedantigo | 6.84 µs | 39 | baseline |
| Playground | 8.06 µs | 33 | 1.18x slower |
| Ozzo | - | - | - |
| Huma | 7.65 µs | 78 | 1.12x slower |
| Godantic | - | - | - |
| Godasse | - | - | - |
Unmarshal_Simple (JSON → validated struct, single call)
| Library | ns/op | allocs | vs Pedantigo |
|---|---|---|---|
| Pedantigo | 3.70 µs | 39 | baseline |
| Playground | - | - | - |
| Ozzo | - | - | - |
| Huma | - | - | - |
| Godantic | 8.46 µs | 81 | 2.28x slower |
| Godasse | 3.46 µs | 42 | 1.07x faster |
Unmarshal_Complex (nested JSON, single call)
| Library | ns/op | allocs | vs Pedantigo |
|---|---|---|---|
| Pedantigo | 11.26 µs | 122 | baseline |
| Playground | - | - | - |
| Ozzo | - | - | - |
| Huma | - | - | - |
| Godantic | 35.96 µs | 285 | 3.19x slower |
| Godasse | 12.25 µs | 149 | 1.09x slower |
Schema_Uncached (first-time generation)
| Library | ns/op | allocs | vs Pedantigo |
|---|---|---|---|
| Pedantigo | 20.01 µs | 227 | baseline |
| Playground | - | - | - |
| Ozzo | - | - | - |
| Huma | 23.37 µs | 255 | 1.17x slower |
| Godantic | - | - | - |
| Godasse | - | - | - |
Schema_Cached (cached lookup)
| Library | ns/op | allocs | vs Pedantigo |
|---|---|---|---|
| Pedantigo | 15 ns | 0 | baseline |
| Playground | - | - | - |
| Ozzo | - | - | - |
| Huma | 466 ns | 6 | 31.44x slower |
| Godantic | - | - | - |
| Godasse | - | - | - |
Generated by pedantigo-benchmarks
Benchmark naming convention
Benchmark_<Library>_<Feature>_<Struct>
Libraries: Pedantigo, Playground, Ozzo, Huma, Godantic, Godasse
Features: Validate, JSONValidate, Marshal, Unmarshal, New, Schema, OpenAPI
Structs: Simple (5 fields), Complex (nested), Large (20+ fields)