Comparison of marshmallow, marshmallow-recipe, and pydantic β performance benchmarks, feature matrix, and installed size.
All libraries operate on the same plain @dataclasses.dataclass types β no library-specific model definitions.
| marshmallow | marshmallow-recipe | mr-recipe nuked | pydantic | |
|---|---|---|---|---|
| Speed (vs marshmallow) | 1x | ~1.3x load / 0.2x dump | 6-17x | 10-30x |
| Install size | 0.4 MB | 1.7 MB | (included) | 8.1 MB |
| Backend | Pure Python | Pure Python | Rust (PyO3) | Rust (pydantic-core) |
| Schema definition | Hand-written | Auto from dataclasses | (same) | Auto (multiple ways) |
| Features | Flexible hooks, plugins | Minimal, ergonomic | (same) | Richest (JSON Schema, OpenAPI, strict mode, generics) |
| Best for | Existing marshmallow codebases | Dataclass-centric projects on marshmallow | (same, need speed) | New projects needing speed + features |
A 10-level-deep Organization hierarchy with lists, nested objects, enums, Decimal, date, datetime, and Optional fields:
L1 Organization
L2 βββ Department
L3 βββ Team
L4 βββ Employee
L5 βββ Contract
L6 β βββ Terms
L7 β βββ Clause
L5 βββ Project
L6 βββ Task
L7 βββ SubTask
L8 βββ Comment
L9 βββ Reaction
L10 βββ Author
Every dataclass has 10-13 fields (str, int, bool, Decimal, date, datetime, Enum, Optional, list).
A single Organization object contains 2 departments x 2 teams x 2 employees, each with 1 contract (2 clauses) and 2 projects (2 tasks each, 2 subtasks each, 1 comment with 2 reactions).
| Library | Version | Backend | How it's used |
|---|---|---|---|
| marshmallow | 3.26.2 | Pure Python | Hand-written Schema classes with @post_load |
| marshmallow-recipe | 0.0.93 | Pure Python | mr.dump(obj) / mr.load(Cls, data) β auto-generated from dataclasses |
| marshmallow-recipe (nuked) | 0.0.93 | Rust (PyO3) | mr.nuked.dump(Cls, obj) / mr.nuked.load(Cls, data) |
| pydantic | 2.12.5 | Rust (pydantic-core) | TypeAdapter(Cls).dump_python(obj) / .validate_python(data) |
| Parameter | Value |
|---|---|
| Date | 2026-04-11 |
| Machine | Apple M1 Pro (8 cores), 32 GB RAM |
| OS | macOS 26.3.1 (arm64) |
| Python | 3.14.0 |
| Timing | Median of adaptive runs (min 10 / max 500 rounds, 10 s budget) |
| GC | gc.collect() before each test, disabled during measurement |
| Scales | 1, 100, 1000 objects per call |
| Total run time | ~15 min |
βββββββββββββββ¬ββββββββββββββ¬ββββββββββββ¬ββββββββββββββββββ¬βββββββββββ
β Operation β marshmallow β mr-recipe β mr-recipe-nuked β pydantic β
βββββββββββββββΌββββββββββββββΌββββββββββββΌββββββββββββββββββΌβββββββββββ€
β dump x 1 β 3.3 ms β 14.6 ms β 538.9 us β 299.0 us β
βββββββββββββββΌββββββββββββββΌββββββββββββΌββββββββββββββββββΌβββββββββββ€
β dump x 100 β 333.0 ms β 1.48 s β 59.7 ms β 32.8 ms β
βββββββββββββββΌββββββββββββββΌββββββββββββΌββββββββββββββββββΌβββββββββββ€
β dump x 1000 β 3.34 s β 15.13 s β 610.0 ms β 342.3 ms β
βββββββββββββββΌββββββββββββββΌββββββββββββΌββββββββββββββββββΌβββββββββββ€
β load x 1 β 13.1 ms β 10.1 ms β 786.5 us β 434.4 us β
βββββββββββββββΌββββββββββββββΌββββββββββββΌββββββββββββββββββΌβββββββββββ€
β load x 100 β 1.28 s β 1.01 s β 82.7 ms β 48.9 ms β
βββββββββββββββΌββββββββββββββΌββββββββββββΌββββββββββββββββββΌβββββββββββ€
β load x 1000 β 12.90 s β 9.95 s β 824.0 ms β 497.3 ms β
βββββββββββββββ΄ββββββββββββββ΄ββββββββββββ΄ββββββββββββββββββ΄βββββββββββ
βββββββββββββββ¬βββββββββββββββ¬βββββββββββββββββββββ¬ββββββββββββββ
β Operation β vs mr-recipe β vs mr-recipe-nuked β vs pydantic β
βββββββββββββββΌβββββββββββββββΌβββββββββββββββββββββΌββββββββββββββ€
β dump x 1 β 1/4.5x β 6.1x β 10.9x β
βββββββββββββββΌβββββββββββββββΌβββββββββββββββββββββΌββββββββββββββ€
β dump x 100 β 1/4.5x β 5.6x β 10.2x β
βββββββββββββββΌβββββββββββββββΌβββββββββββββββββββββΌββββββββββββββ€
β dump x 1000 β 1/4.5x β 5.5x β 9.8x β
βββββββββββββββΌβββββββββββββββΌβββββββββββββββββββββΌββββββββββββββ€
β load x 1 β 1.3x β 16.6x β 30.1x β
βββββββββββββββΌβββββββββββββββΌβββββββββββββββββββββΌββββββββββββββ€
β load x 100 β 1.3x β 15.5x β 26.2x β
βββββββββββββββΌβββββββββββββββΌβββββββββββββββββββββΌββββββββββββββ€
β load x 1000 β 1.3x β 15.7x β 25.9x β
βββββββββββββββ΄βββββββββββββββ΄βββββββββββββββββββββ΄ββββββββββββββ
βββββββββββββββ¬ββββββββββββ¬ββββββββββββββββββ¬ββββββββββ
β Operation β mr-recipe β mr-recipe-nuked β Speedup β
βββββββββββββββΌββββββββββββΌββββββββββββββββββΌββββββββββ€
β dump x 1 β 14.6 ms β 538.9 us β 27.2x β
βββββββββββββββΌββββββββββββΌββββββββββββββββββΌββββββββββ€
β dump x 100 β 1.48 s β 59.7 ms β 24.9x β
βββββββββββββββΌββββββββββββΌββββββββββββββββββΌββββββββββ€
β dump x 1000 β 15.13 s β 610.0 ms β 24.8x β
βββββββββββββββΌββββββββββββΌββββββββββββββββββΌββββββββββ€
β load x 1 β 10.1 ms β 786.5 us β 12.8x β
βββββββββββββββΌββββββββββββΌββββββββββββββββββΌββββββββββ€
β load x 100 β 1.01 s β 82.7 ms β 12.2x β
βββββββββββββββΌββββββββββββΌββββββββββββββββββΌββββββββββ€
β load x 1000 β 9.95 s β 824.0 ms β 12.1x β
βββββββββββββββ΄ββββββββββββ΄ββββββββββββββββββ΄ββββββββββ
βββββββββββββββββββββββββββββββββββββββββ¬βββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β Library (with dependencies) β Total β Breakdown β
βββββββββββββββββββββββββββββββββββββββββΌβββββββββΌβββββββββββββββββββββββββββββββββββββββ€
β marshmallow β 0.4 MB β marshmallow 0.4 MB β
βββββββββββββββββββββββββββββββββββββββββΌβββββββββΌβββββββββββββββββββββββββββββββββββββββ€
β marshmallow-recipe (+ marshmallow) β 1.7 MB β mr-recipe 1.3 MB + marshmallow 0.4 MBβ
βββββββββββββββββββββββββββββββββββββββββΌβββββββββΌβββββββββββββββββββββββββββββββββββββββ€
β pydantic (+ pydantic-core) β 8.1 MB β pydantic 3.8 MB + core 4.4 MB β
βββββββββββββββββββββββββββββββββββββββββ΄βββββββββ΄βββββββββββββββββββββββββββββββββββββββ
Rust binary (.so) sizes:
- marshmallow-recipe
_nuked.soβ 0.7 MB - pydantic-core
_pydantic_core.soβ 4.0 MB
βββββββββββββββ¬ββββββββββββββ¬ββββββββββββ¬ββββββββββββββββββ¬βββββββββββ
β Operation β marshmallow β mr-recipe β mr-recipe-nuked β pydantic β
βββββββββββββββΌββββββββββββββΌββββββββββββΌββββββββββββββββββΌβββββββββββ€
β dump x 1 β 484.5 KB β 1.4 MB β 241.0 KB β 178.0 KB β
βββββββββββββββΌββββββββββββββΌββββββββββββΌββββββββββββββββββΌβββββββββββ€
β dump x 100 β 22.6 MB β 21.9 MB β 21.4 MB β 17.3 MB β
βββββββββββββββΌββββββββββββββΌββββββββββββΌββββββββββββββββββΌβββββββββββ€
β dump x 1000 β 226.1 MB β 217.0 MB β 214.3 MB β 173.1 MB β
βββββββββββββββΌββββββββββββββΌββββββββββββΌββββββββββββββββββΌβββββββββββ€
β load x 1 β 195.2 KB β 193.0 KB β 185.0 KB β 319.4 KB β
βββββββββββββββΌββββββββββββββΌββββββββββββΌββββββββββββββββββΌβββββββββββ€
β load x 100 β 15.5 MB β 15.8 MB β 15.5 MB β 31.1 MB β
βββββββββββββββΌββββββββββββββΌββββββββββββΌββββββββββββββββββΌβββββββββββ€
β load x 1000 β 154.4 MB β 157.6 MB β 155.1 MB β 311.4 MB β
βββββββββββββββ΄ββββββββββββββ΄ββββββββββββ΄ββββββββββββββββββ΄βββββββββββ
| Feature | marshmallow | marshmallow-recipe | pydantic |
|---|---|---|---|
| Hand-written schemas | Yes | β | Yes (BaseModel) |
| Auto from dataclasses | β | Yes | Yes (TypeAdapter) |
| Auto from TypedDict | β | β | Yes |
| Dynamic model creation | β | β | Yes (create_model()) |
| Generic models | β | β | Yes (Generic[T]) |
| Computed fields | β | β | Yes (@computed_field) |
| Feature | marshmallow | marshmallow-recipe | pydantic |
|---|---|---|---|
| Built-in validators | Range, Length, OneOf, NoneOf, Equal, Regexp, URL, Email | regexp, email | All via Annotated constraints (gt, lt, min_length, pattern, ...) |
| Custom validators | @validates, @validates_schema |
mr.validate(fn) |
@field_validator, @model_validator |
| Before/after modes | β | β | Yes (mode='before'/'after'/'wrap') |
| Strict mode | β | β | Yes (global or per-field) |
| Validate on assignment | β | β | Yes (validate_assignment=True) |
| Validate defaults | β | β | Yes (validate_default=True) |
| Fail fast (stop on first error) | β | β | Yes (FailFast) |
| Feature | marshmallow | marshmallow-recipe | pydantic |
|---|---|---|---|
| Dump to dict | schema.dump(obj) |
mr.dump(obj) |
adapter.dump_python(obj) |
| Load from dict | schema.load(data) |
mr.load(Cls, data) |
adapter.validate_python(data) |
| Dump to JSON string | β | β | Yes (dump_json()) |
| Load from JSON string | β | β | Yes (validate_json()) |
| Load from string values | β | β | Yes (validate_strings()) |
| Many (batch) | many=True |
dump_many() / load_many() |
Yes (via TypeAdapter(list[T])) |
| Partial loading | Yes (partial=True) |
β | β |
| Nested objects | Yes (Nested()) |
Yes (auto) | Yes (auto) |
| Feature | marshmallow | marshmallow-recipe | pydantic |
|---|---|---|---|
pre_load |
Yes | Yes (@mr.pre_load) |
Yes (mode='before') |
post_load |
Yes | β | Yes (model_post_init) |
pre_dump |
Yes | β | β |
post_dump |
Yes | β | β |
| Type | marshmallow | marshmallow-recipe | pydantic |
|---|---|---|---|
| str, int, float, bool | Yes | Yes | Yes |
| Decimal | Yes | Yes | Yes |
| datetime, date, time | Yes | Yes | Yes |
| timedelta | Yes | β | Yes |
| UUID | Yes | Yes | Yes |
| Enum (str/int) | Yes | Yes | Yes |
| list, set, frozenset | Yes | Yes | Yes |
| dict | Yes | Yes | Yes |
| tuple (homogeneous) | Yes | Yes | Yes |
| tuple (heterogeneous) | Yes | Standard only | Yes |
| Union | β | Yes | Yes |
| Discriminated union | β | β | Yes |
| Literal | β | Yes | Yes |
| Optional | Yes | Yes | Yes |
| bytes | Yes | Yes | Yes |
| IP address / network | Yes | β | Yes |
| URL | Yes | β | Yes |
| Email (validated) | Yes | Yes (validator) | Yes (EmailStr) |
| FilePath / DirectoryPath | β | β | Yes |
| SecretStr / SecretBytes | β | β | Yes |
| Json (raw) | β | Yes (JsonRawField) |
Yes (Json) |
| Base64 | β | β | Yes |
| PaymentCardNumber | β | β | Yes |
| Feature | marshmallow | marshmallow-recipe | pydantic |
|---|---|---|---|
| Per-field alias | Yes (data_key) |
Yes (mr.meta(name=...)) |
Yes (Field(alias=...)) |
| Separate validation/serialization alias | β | β | Yes |
| camelCase convention | Manual | Yes (CAMEL_CASE) |
Yes (alias_generator) |
| UPPER_SNAKE_CASE | Manual | Yes (UPPER_SNAKE_CASE) |
Yes (alias_generator) |
| CapitalCamelCase | Manual | Yes (CAPITAL_CAMEL_CASE) |
Yes (alias_generator) |
| Feature | marshmallow | marshmallow-recipe | pydantic |
|---|---|---|---|
| JSON Schema generation | Via plugin | Yes (mr.json_schema()) |
Yes (built-in) |
| OpenAPI support | Via apispec | β | Yes (built-in) |
| Settings management | β | β | Via pydantic-settings |
None value handling |
β | Yes (NoneValueHandling) |
Yes (via serialization options) |
| Unknown fields handling | RAISE / EXCLUDE / INCLUDE | β | 'forbid' / 'ignore' / 'allow' |
Performance
- pydantic v2 is the fastest overall (10-30x over marshmallow), powered by its Rust core
- marshmallow-recipe nuked is a strong second (6-17x over marshmallow), powered by Rust via PyO3
- marshmallow-recipe standard (
mr.dump/mr.load) is ~4.5x slower than raw marshmallow for dump, ~1.3x faster for load - The nuked backend of marshmallow-recipe is 12-27x faster than its standard backend
- Performance gaps widen with deeper nesting compared to flat models
Peak memory
- For dump, all libraries use similar memory at scale; pydantic is the most memory-efficient (~173 MB for 1000 objects vs ~226 MB for marshmallow)
- For load, pydantic uses 2x more memory than the others (311 MB vs ~155 MB for 1000 objects) β the Rust core creates richer internal representations
- At small scale (x 1), all libraries are lightweight (<500 KB), except mr-recipe standard dump (1.4 MB)
Installed size
- marshmallow is the lightest at 0.4 MB β pure Python, zero compiled code
- marshmallow-recipe is 1.7 MB total (with marshmallow); its Rust binary is only 0.7 MB β 5.7x smaller than pydantic-core's
- pydantic is the heaviest at 8.1 MB (with pydantic-core) β nearly 5x larger than marshmallow-recipe, though the Rust core is what makes it the fastest
Features
- pydantic has the richest feature set: JSON Schema, OpenAPI, strict mode, discriminated unions, computed fields, generics, direct JSON string serialization, and the widest field type coverage
- marshmallow offers the most flexible hook system (pre/post load/dump), partial loading, and a mature plugin ecosystem (apispec, marshmallow-jsonschema)
- marshmallow-recipe is the most ergonomic for dataclass-heavy codebases: zero boilerplate schema definitions, built-in naming conventions (camelCase, UPPER_SNAKE), and JSON Schema generation β all while staying compatible with the marshmallow ecosystem
When to use what
- pydantic β best overall choice when performance, features, and JSON/OpenAPI integration matter most, and install size is not a concern
- marshmallow-recipe (nuked) β best for dataclass-centric projects that need fast serialization with a small footprint, especially when already using marshmallow
- marshmallow β best when you need maximum control over schema behavior, custom hooks, or have an existing marshmallow-based codebase
make benchThis uses uv to auto-install dependencies and run the benchmark.
Or manually:
uv run bench.py