Skip to content

Latest commit

History

History

README.md

BPlusTree - Python Implementation

A high-performance B+ tree implementation for Python with competitive performance against highly optimized libraries like SortedDict.

🚀 Quick Start

Installation

Option 1: Install from source (current)

git clone https://github.com/KentBeck/BPlusTree.git
cd BPlusTree/python
pip install -e .

Option 2: Install from PyPI (coming soon)

pip install bplustree

Requirements

  • Python 3.8 or higher
  • C compiler (for C extension, optional)

Implementation Selection

The library automatically selects the best available implementation:

  1. C Extension (preferred): 2-4x faster, used automatically if available
  2. Pure Python: Fallback implementation, no compilation required

Check which implementation is being used:

frombplustreeimportget_implementationprint(get_implementation()) # "C extension" or "Pure Python"

📖 Basic Usage

frombplustreeimportBPlusTreeMap# Create a B+ treetree=BPlusTreeMap(capacity=128) # Higher capacity = better performance# Insert datatree[1] ="one"tree[3] ="three"tree[2] ="two"# Lookupsprint(tree[2]) # "two"print(len(tree)) # 3print(2intree) # True# Range queriesforkey, valueintree.range(1, 3):
print(f"{key}: {value}")
# Iterationforkey, valueintree.items():
print(f"{key}: {value}")

⚡ Performance Highlights

Our benchmarks against SortedDict show significant advantages in specific scenarios:

🏆 Where B+ Tree Excels

ScenarioB+ Tree AdvantageUse Cases
Partial Range ScansUp to 2.5x fasterDatabase LIMIT queries, pagination
Large Dataset Iteration1.1x - 1.4x fasterData export, bulk processing
Medium Range Queries1.4x fasterTime-series analysis, batch processing

📊 Benchmark Results

Partial Range Scans (Early Termination):

Limit 10 items: B+ Tree 1.18x faster
Limit 50 items: B+ Tree 2.50x faster ⭐ Best performance
Limit 100 items: B+ Tree 1.52x faster
Limit 500 items: B+ Tree 1.15x faster

Large Dataset Iteration:

200K items: B+ Tree 1.29x faster
300K items: B+ Tree 1.12x faster
500K items: B+ Tree 1.39x faster ⭐ Scales well

Optimal Configuration:

  • Capacity 128 provides best performance (3.3x faster than capacity 4)
  • Performance continues improving with larger capacities

🎯 When to Choose B+ Tree

Excellent for:

  • Database-like workloads with range queries
  • Analytics dashboards ("top 100 users")
  • Search systems with pagination
  • Time-series data processing
  • Data export and ETL operations
  • Any scenario with "LIMIT" or early termination patterns

Use SortedDict when:

  • Random access dominates (37x faster individual lookups)
  • Small datasets (< 100K items)
  • Memory efficiency is critical
  • General-purpose sorted container needs

🔧 Configuration

# Small capacity: More splits, good for testingtree=BPlusTree(capacity=4)
# Medium capacity: Balanced performancetree=BPlusTree(capacity=16)
# Large capacity: Optimal for most use casestree=BPlusTree(capacity=128) # Recommended!

🧪 Testing

# Run tests
python -m pytest tests/
# Run performance benchmarks
python tests/test_performance_vs_sorteddict.py
# Run specific tests
python -m pytest tests/test_bplustree.py -v

📖 API Reference

Basic Operations

tree=BPlusTree(capacity=128)
# Dictionary-like interfacetree[key] =valuevalue=tree[key] # Raises KeyError if not founddeltree[key] # Raises KeyError if not foundkeyintree# Returns boollen(tree) # Returns int# Safe operationstree.get(key, default=None)
tree.pop(key, default=None)

Iteration and Ranges

# Full iterationforkey, valueintree.items():
passforkeyintree.keys():
passforvalueintree.values():
pass# Range queriesforkey, valueintree.range(start_key, end_key):
pass# Range with None boundsforkey, valueintree.range(start_key, None): # From start_key to endpassforkey, valueintree.range(None, end_key): # From beginning to end_keypass

🔒 Iterator Safety

The C extension provides iterator safety to prevent segmentation faults during tree modifications:

tree=BPlusTree(capacity=128)
foriinrange(10):
tree[i] =f"value_{i}"# Create iteratorkeys_iter=tree.keys()
first_key=next(keys_iter)
# Modify tree during iterationtree[100] ="new_value"# Iterator detects modification and raises RuntimeErrortry:
next(keys_iter)
exceptRuntimeErrorase:
print(e) # "tree changed size during iteration"

Safety Features:

  • Modification detection: Iterators track tree changes via internal counter
  • Graceful failure: RuntimeError instead of segmentation fault
  • Multiple iterator support: All active iterators are invalidated on modification
  • Consistent behavior: Matches Python's dict iterator safety model

Safe Patterns:

# ✅ Safe: Complete iteration before modificationkeys=list(tree.keys()) # Collect all keys firstforkeyinkeys:
tree[key] =new_value# ✅ Safe: Use fresh iterator after modificationstree[new_key] =new_valueforkey, valueintree.items(): # New iterator, safe to useprocess(key, value)

🏗️ Architecture

  • Arena-based memory management for efficiency
  • Linked leaf nodes for fast sequential access
  • Optimized rebalancing algorithms
  • Hybrid navigation for range queries
  • Iterator safety with modification counter tracking

📚 Documentation & Examples

🔗 Links

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.