BitChat Performance Characteristics: Benchmarks, Throughput Metrics, and Architectural Optimizations
BitChat achieves sustained throughputs of 11,000 BLE packets/sec for local mesh communication and 2,000 Nostr events/sec for global relay processing, with sub-millisecond latency for deduplication and filter operations.
The BitChat message routing system, maintained in permissionlesstech/bitchat, is engineered for high-throughput, low-latency communication across dual transport layers. Its performance characteristics emerge from a combination of zero-copy binary protocols, adaptive battery management, and carefully optimized data structures that minimize CPU and memory overhead on mobile devices.
Core Performance Goals and Design Philosophy
BitChat operates simultaneously on two network layers with distinct performance requirements:
- Bluetooth LE mesh for local, offline-capable communication with tight latency budgets
- Nostr protocol for global, censorship-resistant message relay
The architecture prioritizes fast local communication through a binary protocol that avoids memory copies wherever possible. In localPackages/BitFoundation/Sources/BitFoundation/BinaryProtocol.swift, the BinaryProtocol implementation uses Swift Data views to encode and decode packets without allocating new buffers.
Battery efficiency is equally critical. Adaptive battery modes dynamically throttle BLE scanning and transmission when devices are idle, extending operational time without sacrificing responsiveness when active.
Performance Baseline Benchmarks
The PerformanceBaselineTests suite in bitchatTests/Performance/PerformanceBaselineTests.swift establishes quantitative throughput floors for every critical code path. These benchmarks run in CI and enforce performance regressions via scripts/check-perf-floors.sh.
Nostr Inbound Processing
| Benchmark | Path | Throughput |
|---|---|---|
| Nostr inbound (fresh events) | Full processing: deduplication, geo-hash validation, scheduling | ~2,000 events/sec |
| Nostr inbound (duplicate events) | Fast-path deduplication without cryptographic operations | ~30,000 events/sec |
The 15x throughput differential between fresh and duplicate events highlights the cost of cryptographic validation and state management versus the O(1) deduplication check. The MessageDeduplicator stores compact 64-bit keys enabling constant-time duplicate detection.
BLE Mesh Communication
| Benchmark | Operation | Throughput |
|---|---|---|
| BLE inbound packet pipeline | Decode + deduplication of 100-300 byte packets | ~11,000 packets/sec |
This measurement covers the complete path from raw Data through BitchatPacket.from(_:) to deduplication key generation. The zero-copy binary protocol eliminates buffer allocations that would otherwise dominate latency.
Data Structure Operations
| Benchmark | What It Measures | Throughput |
|---|---|---|
| GCS filter build & decode | Golomb-Coded Set construction and parsing for 1,000 packet IDs | ~140 filters/sec |
| Delivery status (coordinator) | Incremental updates through DeliveryStatusCoordinator |
~9,000 updates/sec |
| Delivery status (store direct) | Direct ConversationStore updates |
~13,000 updates/sec |
The coordinator overhead reduces throughput by approximately 30%, reflecting the cost of cross-component synchronization versus direct store access.
Message Processing Pipelines
| Benchmark | Scope | Throughput |
|---|---|---|
| Message formatting | 200 messages with mentions, hashtags, URL detection | ~1,000 messages/sec |
| Private ingest pipeline | Full ChatViewModel path for private messages |
~1,000 messages/sec |
| Public ingest pipeline | Full path with 50 distinct senders | ~900 messages/sec |
Public message processing incurs slightly higher overhead due to sender validation and provenance tracking across multiple identities.
ConversationStore Performance
| Benchmark | Scenario | Throughput |
|---|---|---|
| ConversationStore append | 1,000 out-of-order inserts with ordering maintenance | ~5,000 messages/sec |
| Steady-state append | Append under 1,337-message retention cap with eviction | ~12,000 messages/sec |
| ConversationStore audit | Invariant validation over 5,000-message corpus | ~80 audits/sec |
The 2.4x throughput increase in steady-state operation reflects streamlined eviction paths versus the binary search and insertion overhead of out-of-order appends.
Architectural Optimizations
Zero-Copy Binary Protocol
The BinaryProtocol implementation at BinaryProtocol.swift#L84-L86 uses Data.subdata(in:) and withUnsafeBytes to parse packet fields without copying underlying bytes. This eliminates heap allocation pressure during high-frequency BLE operations.
LZ4 Compression
Outgoing BLE payloads undergo LZ4 compression, reducing transmission time and radio-on duration. The compression ratio typically achieves 2-3x reduction for text-heavy messages, directly improving battery life.
Noise Protocol Encryption
Mesh encryption uses the Noise protocol via NoiseEncryptionService.swift#L78, providing forward secrecy with minimal overhead. The handshake completes in single round-trip with small message expansion, unlike TLS which would require multiple exchanges and certificate overhead.
Golomb-Coded Set Filters
GCS filters enable bandwidth-efficient state synchronization between mesh nodes. Each filter represents 1,000 packet IDs in ~150 bytes, allowing nodes to identify missing messages with sub-millisecond set operations rather than transmitting full inventories.
Single-Source-of-Truth Store
All message state converges on ConversationStore, eliminating redundant indices. Ordered insertion uses binary search on a sorted array rather than tree structures, optimizing for cache locality and Swift's copy-on-write semantics.
Adaptive Battery Management
BLE scan intervals scale from continuous (active conversation) to 10-second windows (idle), reducing radio power consumption by up to 90% during quiescent periods without breaking mesh connectivity.
Running and Interpreting Benchmarks
Execute Performance Tests Locally
# Run complete suite including performance baselines
swift test
# Skip performance tests for faster iteration
BITCHAT_SKIP_PERF_BASELINES=1 swift test
# Capture structured performance output
BITCHAT_PERF_LOG=perf.log swift test --quiet --filter PerformanceBaselineTests
The BITCHAT_PERF_LOG environment variable triggers formatted output parsing by scripts/check-perf-floors.sh.
Parse Benchmark Output
cat perf.log | grep "PERF\[store.append\]"
# Output: PERF[store.append]: 5400 messages/sec (avg 0.185 ms per pass of 1000, 5 passes)
Interpretation:
- Operations per pass: 1,000
appendcalls - Average latency: 0.185 ms per operation
- Sustainable throughput: 5,400 operations/second
Direct Store Usage Pattern
import BitFoundation
let store = ConversationStore()
let message = BitchatMessage(
id: "msg-\(UUID().uuidString.prefix(8))",
sender: "alice",
content: "Zero-copy mesh message",
timestamp: Date(),
isRelay: false
)
// O(log n) ordered insert with binary search positioning
store.append(message, to: .mesh)
This pattern mirrors the benchmarked path in testConversationStoreAppend at PerformanceBaselineTests.swift#L268-L304.
BLE Packet Processing Micro-Benchmark
import XCTest
@testable import bitchat
func measureBLEThroughput() {
let deduplicator = MessageDeduplicator()
let packets = (0..<1000).map { _ in generateRandomPacket() }
let start = Date()
for packet in packets {
guard let data = packet.toBinaryData(),
let decoded = BitchatPacket.from(data) else { continue }
let key = dedupKey(packet: decoded)
_ = deduplicator.isDuplicate(key: key)
}
let elapsed = Date().timeIntervalSince(start)
print("Throughput: \(Double(packets.count) / elapsed) packets/sec")
}
This replicates the core measurement from testBLEInboundPacketPipeline at PerformanceBaselineTests.swift#L38-L44.
Key Source Files
| File | Purpose |
|---|---|
bitchatTests/Performance/PerformanceBaselineTests.swift |
Complete benchmark suite with throughput floors |
localPackages/BitFoundation/Sources/BitFoundation/BinaryProtocol.swift |
Zero-copy binary encoding/decoding |
bitchat/Services/NoiseEncryptionService.swift |
Noise protocol mesh encryption |
bitchat/ViewModels/ChatViewModel.swift |
Message ingest pipeline orchestration |
scripts/check-perf-floors.sh |
CI validation of benchmark regressions |
README.md |
High-level architecture and feature overview |
Summary
- BitChat achieves 11,000 BLE packets/sec for local mesh deduplication and decoding via zero-copy binary protocols
- Nostr processing reaches 2,000 fresh events/sec (30,000/sec for duplicates) through optimized deduplication paths
- ConversationStore sustains 5,000-12,000 appends/sec depending on ordering constraints and retention policies
- LZ4 compression, Noise protocol encryption, and GCS filters collectively minimize bandwidth, latency, and battery consumption
- Adaptive battery modes dynamically balance responsiveness against power efficiency
- All performance claims are enforced in CI via
PerformanceBaselineTestsandcheck-perf-floors.sh
Frequently Asked Questions
What throughput can I expect for BLE mesh messaging?
Expect approximately 11,000 packets per second for packet decoding and deduplication on modern hardware. This covers the complete path from raw Data through BitchatPacket.from(_:) to duplicate detection. Real-world mobile performance is lower due to radio scheduling and OS constraints, but remains well within interactive latency budgets.
How does BitChat handle duplicate messages efficiently?
The MessageDeduplicator class provides O(1) duplicate detection using compact 64-bit keys stored in a hash set. Duplicate events bypass expensive cryptographic validation entirely, achieving 30,000 events/sec versus 2,000/sec for fresh events that require full processing.
Why is steady-state append faster than initial append in ConversationStore?
Steady-state append (12,000 messages/sec) operates under a fixed 1,337-message retention cap with streamlined eviction logic. Initial append (5,000 messages/sec) handles out-of-order inserts requiring binary search positioning and array expansion. The steady-state path avoids these ordering overheads.
How do I run performance benchmarks in my own environment?
Set BITCHAT_PERF_LOG=perf.log and execute swift test --filter PerformanceBaselineTests. The output includes formatted throughput lines like PERF[category.metric]: X ops/sec that scripts/check-perf-floors.sh can validate against expected floors defined in the test source.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →