Skip to content

Commit 1813409

Browse files
committed
Convert bytes to bson, add EVG infra
1 parent 5a99d9b commit 1813409

File tree

5 files changed

+574
-82
lines changed

5 files changed

+574
-82
lines changed

.evergreen/generated_configs/tasks.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2554,6 +2554,21 @@ tasks:
25542554
- func: attach benchmark test results
25552555
- func: send dashboard data
25562556
tags: [perf]
2557+
- name: perf-8.0-standalone-ssl-rust
2558+
commands:
2559+
- func: run server
2560+
vars:
2561+
VERSION: v8.0-perf
2562+
SSL: ssl
2563+
- func: run tests
2564+
vars:
2565+
TEST_NAME: perf
2566+
SUB_TEST_NAME: rust
2567+
PYMONGO_BUILD_RUST: "1"
2568+
PYMONGO_USE_RUST: "1"
2569+
- func: attach benchmark test results
2570+
- func: send dashboard data
2571+
tags: [perf]
25572572
- name: perf-8.0-standalone
25582573
commands:
25592574
- func: run server
@@ -2580,6 +2595,21 @@ tasks:
25802595
- func: attach benchmark test results
25812596
- func: send dashboard data
25822597
tags: [perf]
2598+
- name: perf-8.0-standalone-rust
2599+
commands:
2600+
- func: run server
2601+
vars:
2602+
VERSION: v8.0-perf
2603+
SSL: nossl
2604+
- func: run tests
2605+
vars:
2606+
TEST_NAME: perf
2607+
SUB_TEST_NAME: rust
2608+
PYMONGO_BUILD_RUST: "1"
2609+
PYMONGO_USE_RUST: "1"
2610+
- func: attach benchmark test results
2611+
- func: send dashboard data
2612+
tags: [perf]
25832613

25842614
# Rust extension tests
25852615
- name: test-rust-latest-python3.10-noauth-nossl-standalone

.evergreen/rust-extension.yml

Lines changed: 0 additions & 64 deletions
This file was deleted.

.evergreen/scripts/generate_config.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -958,18 +958,24 @@ def create_search_index_tasks():
958958

959959
def create_perf_tasks():
960960
tasks = []
961-
for version, ssl, sync in product(["8.0"], ["ssl", "nossl"], ["sync", "async"]):
961+
for version, ssl, sync in product(["8.0"], ["ssl", "nossl"], ["sync", "async", "rust"]):
962962
vars = dict(VERSION=f"v{version}-perf", SSL=ssl)
963963
server_func = FunctionCall(func="run server", vars=vars)
964-
vars = dict(TEST_NAME="perf", SUB_TEST_NAME=sync)
965-
test_func = FunctionCall(func="run tests", vars=vars)
964+
test_vars = dict(TEST_NAME="perf", SUB_TEST_NAME=sync)
965+
# Enable Rust for rust perf tests
966+
if sync == "rust":
967+
test_vars["PYMONGO_BUILD_RUST"] = "1"
968+
test_vars["PYMONGO_USE_RUST"] = "1"
969+
test_func = FunctionCall(func="run tests", vars=test_vars)
966970
attach_func = FunctionCall(func="attach benchmark test results")
967971
send_func = FunctionCall(func="send dashboard data")
968972
task_name = f"perf-{version}-standalone"
969973
if ssl == "ssl":
970974
task_name += "-ssl"
971975
if sync == "async":
972976
task_name += "-async"
977+
elif sync == "rust":
978+
task_name += "-rust"
973979
tags = ["perf"]
974980
commands = [server_func, test_func, attach_func, send_func]
975981
tasks.append(EvgTask(name=task_name, tags=tags, commands=commands))

bson/_rbson/README.md

Lines changed: 88 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -183,20 +183,94 @@ When these missing features were added to achieve 100% compatibility, the true p
183183
- Edge case handling for all 88 tests
184184
3. **The Fundamental Issue**: Both implementations suffer from the same architectural limitation (Python → Bson enum → bytes), but it only becomes a significant bottleneck when you implement all the features required for production use.
185185

186+
## Direct Byte-Writing Performance Results
187+
188+
### Implementation: `_dict_to_bson_direct()`
189+
190+
A new implementation has been added that writes BSON bytes directly from Python objects without converting to `Bson` enum types first. This eliminates the intermediate conversion layer.
191+
192+
**Architecture Comparison:**
193+
```
194+
Regular: Python objects → Rust Bson enum → BSON bytes
195+
Direct: Python objects → BSON bytes (no intermediate types)
196+
```
197+
198+
### Benchmark Results
199+
200+
Comprehensive benchmarks on realistic document types show **consistent 2x speedup**:
201+
202+
| Document Type | Regular (ops/sec) | Direct (ops/sec) | Speedup |
203+
|--------------|-------------------|------------------|---------|
204+
| User Profile | 99,970 | 208,658 | **2.09x** |
205+
| E-commerce Order | 93,578 | 165,636 | **1.77x** |
206+
| IoT Sensor Data | 136,824 | 312,058 | **2.28x** |
207+
| Blog Post | 65,782 | 134,154 | **2.04x** |
208+
209+
**Average Speedup: 2.04x** (range: 1.77x - 2.28x)
210+
211+
### Performance by Document Composition
212+
213+
| Document Type | Regular (ops/sec) | Direct (ops/sec) | Speedup |
214+
|--------------|-------------------|------------------|---------|
215+
| Simple types (int, str, float, bool, None) | 177,588 | 800,670 | **4.51x** |
216+
| Mixed types | 223,856 | 342,305 | **1.53x** |
217+
| Nested documents | 130,884 | 287,758 | **2.20x** |
218+
| BSON-specific types only | 342,059 | 304,844 | 0.89x |
219+
220+
### Key Findings
221+
222+
1. **Massive speedup for simple types**: 4.51x faster for documents with Python native types
223+
2. **Consistent 2x improvement for real-world documents**: All realistic mixed-type documents show 1.77x - 2.28x speedup
224+
3. **Slight slowdown for pure BSON types**: Documents with only BSON-specific types (ObjectId, Binary, etc.) are 10% slower due to extra Python attribute lookups
225+
4. **100% correctness**: All outputs verified to be byte-identical to the regular implementation
226+
227+
### Why Direct Byte-Writing is Faster
228+
229+
1. **Eliminates heap allocations**: No need to create intermediate `Bson` enum values
230+
2. **Reduces function call overhead**: Writes bytes immediately instead of going through `python_to_bson()``write_bson_value()`
231+
3. **Better for common types**: Python's native types (int, str, float, bool) can be written directly without any conversion
232+
233+
### Implementation Details
234+
235+
The direct approach is implemented in these functions:
236+
- `_dict_to_bson_direct()` - Public API function
237+
- `write_document_bytes_direct()` - Writes document structure directly
238+
- `write_element_direct()` - Writes individual elements without Bson conversion
239+
- `write_bson_type_direct()` - Handles BSON-specific types directly
240+
241+
### Usage
242+
243+
```python
244+
from bson import _rbson
245+
from bson.codec_options import DEFAULT_CODEC_OPTIONS
246+
247+
# Use direct byte-writing approach
248+
doc = {"name": "John", "age": 30, "score": 95.5}
249+
bson_bytes = _rbson._dict_to_bson_direct(doc, False, DEFAULT_CODEC_OPTIONS)
250+
```
251+
252+
### Benchmarking
253+
254+
Run the benchmarks yourself:
255+
```bash
256+
python benchmark_direct_bson.py # Quick comparison
257+
python benchmark_bson_types.py # Individual type analysis
258+
python benchmark_comprehensive.py # Detailed statistics
259+
```
260+
186261
## Steps to Achieve Performance Parity with C Extensions
187262

188-
Based on the analysis in PR #2695, here are the steps needed to match C extension performance:
263+
Based on the analysis in PR #2695 and the direct byte-writing results, here are the steps needed to match C extension performance:
189264

190-
### 1. Eliminate Intermediate Bson Enum (High Impact)
265+
### 1. Eliminate Intermediate Bson Enum (High Impact) - COMPLETED
191266
**Current**: Python → Bson → bytes
192267
**Target**: Python → bytes (direct)
193268

194-
Implement direct BSON byte writing from Python objects without converting to `Bson` enum first. This would require:
195-
- Custom serialization logic for each Python type
196-
- Manual BSON format handling (type bytes, length prefixes, etc.)
197-
- Bypassing the `bson` crate's serialization layer
269+
**Status**: ✅ **Implemented as `_dict_to_bson_direct()`**
270+
271+
**Actual Impact**: **2.04x average speedup** on realistic documents (range: 1.77x - 2.28x)
198272

199-
**Estimated Impact**: 3-4x performance improvement
273+
This brings the Rust extension from ~0.21x (5x slower than C) to **~0.43x (2.3x slower than C)** - a significant improvement!
200274

201275
### 2. Optimize Python API Calls (Medium Impact)
202276
- Reduce `getattr()` calls by caching attribute lookups
@@ -220,15 +294,14 @@ Implement direct BSON byte writing from Python objects without converting to `Bs
220294

221295
**Estimated Impact**: 1.05-1.1x performance improvement
222296

223-
### Combined Potential
224-
If all optimizations are implemented successfully:
225-
- Current: 0.21x (5x slower)
226-
- Target: 0.21x × 3.5 × 1.3 × 1.2 × 1.05 = **~1.13x** (13% faster than C)
297+
### Combined Potential (Updated with Direct Byte-Writing Results)
298+
With direct byte-writing implemented:
299+
- **Before**: 0.21x (5x slower than C)
300+
- **After direct byte-writing**: 0.43x (2.3x slower than C) ✅
301+
- **With all optimizations**: 0.43x × 1.3 × 1.2 × 1.05 = **~0.71x** (1.4x slower than C)
302+
- **Optimistic target**: Could potentially reach **~0.9x - 1.0x** (parity with C)
227303

228-
However, achieving this would require:
229-
- Significant engineering effort (weeks to months)
230-
- Bypassing the `bson` crate (losing its benefits)
231-
- Complex low-level code (harder to maintain)
304+
The direct byte-writing approach has already delivered the largest performance gain (2x). Additional optimizations could close the remaining gap to C extension performance.
232305

233306
## Building
234307

0 commit comments

Comments
 (0)