# BitChat Performance Characteristics: Throughput Benchmarks and Architectural Optimizations Explained

> Explore BitChat performance with throughput benchmarks up to 30,000 events/sec. Learn about architectural optimizations like zero-copy protocols and adaptive scheduling.

- Repository: [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat)
- Tags: performance
- Published: 2026-08-20

---

**BitChat achieves up to 30,000 events/second for Nostr deduplication, 11,000 packets/second for BLE mesh processing, and sub-millisecond GCS filter operations through a combination of zero-copy protocols, LZ4 compression, and adaptive battery-aware scheduling.**

The [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat) repository implements a dual-transport messaging protocol that bridges local Bluetooth-LE mesh networks with the global Nostr relay network. Understanding BitChat performance characteristics requires examining both the micro-benchmarks that define its throughput floors and the architectural decisions that enable efficient operation on resource-constrained mobile devices.

## Performance Benchmarks from PerformanceBaselineTests

The [`PerformanceBaselineTests.swift`](https://github.com/permissionlesstech/bitchat/blob/main/PerformanceBaselineTests.swift) suite establishes concrete throughput guarantees for critical code paths. These benchmarks run in CI with parallelism enabled to measure theoretical maximums rather than contention-limited behavior.

### Nostr Inbound Processing

| Path | Throughput | Description |
|------|-----------|-------------|
| **Fresh events** | ~2,000 events/sec | Full processing including deduplication, bookkeeping, and scheduling for newly-seen geo-hash events |
| **Duplicate events** | ~30,000 events/sec | Fast-path deduplication for already-processed events, skipping cryptographic verification |

The dramatic difference between fresh and duplicate paths demonstrates the effectiveness of BitChat's deduplication layer. As implemented in `testNostrInboundFresh` and `testNostrInboundDuplicate` at [PerformanceBaselineTests.swift#L14-L104](https://github.com/permissionlesstech/bitchat/blob/main/bitchatTests/Performance/PerformanceBaselineTests.swift), the duplicate path avoids all cryptographic operations and operates purely on compact key comparisons.

### BLE Mesh Processing

The Bluetooth-LE pipeline measured in `testBLEInboundPacketPipeline` achieves **~11,000 packets/second** for decode-plus-deduplication of 100-300 byte packets. This benchmark at [PerformanceBaselineTests.swift#L38-L92](https://github.com/permissionlesstech/bitchat/blob/main/bitchatTests/Performance/PerformanceBaselineTests.swift) exercises:

- Binary protocol decoding via `BitchatPacket.from()`
- `MessageDeduplicator.isDuplicate()` calls with O(1) key lookup
- Round-trip validation without actual radio transmission

### GCS Filter Operations

Golomb-Coded Set filters enable efficient mesh state synchronization with **~140 filters/second** build-and-decode throughput for 1,000 packet ID sets. The benchmark at [PerformanceBaselineTests.swift#L94-L122](https://github.com/permissionlesstech/bitchat/blob/main/bitchatTests/Performance/PerformanceBaselineTests.swift) validates the implementation in [`GCSFilter.swift`](https://github.com/permissionlesstech/bitchat/blob/main/GCSFilter.swift), which provides sub-millisecond membership tests that limit bandwidth during peer synchronization.

### Message Delivery Status Updates

| Component | Throughput | Notes |
|-----------|-----------|-------|
| **Coordinator path** | ~9,000 updates/sec | Through `DeliveryCoordinator.incrementalUpdate()` with full orchestration |
| **Direct store path** | ~13,000 updates/sec | Bypassing coordinator overhead for raw store mutations |

These benchmarks at [PerformanceBaselineTests.swift#L24-L94](https://github.com/permissionlesstech/bitchat/blob/main/bitchatTests/Performance/PerformanceBaselineTests.swift) demonstrate the cost abstraction: ~30% overhead for coordinator-mediated updates versus direct store access.

### Message Processing Pipelines

- **Private ingest**: ~1,000 messages/second through `ChatViewModel` full private-message pipeline ([PerformanceBaselineTests.swift#L148-L200](https://github.com/permissionlesstech/bitchat/blob/main/bitchatTests/Performance/PerformanceBaselineTests.swift))
- **Public ingest**: ~900 messages/second with 50-sender multiplexing ([PerformanceBaselineTests.swift#L202-L266](https://github.com/permissionlesstech/bitchat/blob/main/bitchatTests/Performance/PerformanceBaselineTests.swift))
- **Message formatting**: ~1,000 messages/second with mention/hashtag/URL parsing ([PerformanceBaselineTests.swift#L96-L146](https://github.com/permissionlesstech/bitchat/blob/main/bitchatTests/Performance/PerformanceBaselineTests.swift))

The private/public throughput gap reflects additional validation and routing logic for public channel messages from multiple senders.

### ConversationStore Append Performance

| Scenario | Throughput | Characteristics |
|----------|-----------|---------------|
| **General append** | ~5,000 messages/sec | 1,000 appends with out-of-order inserts, binary-search positioning |
| **Steady-state append** | ~12,000 messages/sec | Appends under 1,337-message retention cap with eviction path |

The steady-state benchmark at [PerformanceBaselineTests.swift#L306-L360](https://github.com/permissionlesstech/bitchat/blob/main/bitchatTests/Performance/PerformanceBaselineTests.swift) demonstrates that the eviction path—removing oldest messages to maintain the retention window—actually outperforms general insertion due to reduced sorting overhead for append-mostly workloads.

### Store Audit Throughput

The invariant checker at [PerformanceBaselineTests.swift#L362-L388](https://github.com/permissionlesstech/bitchat/blob/main/bitchatTests/Performance/PerformanceBaselineTests.swift) achieves **~80 audits/second** over 5,000-message corpora. This validates timestamp ordering, ID uniqueness, and retention compliance—suitable for debug builds but disabled in production.

## Architectural Optimizations Enabling BitChat Performance

### Zero-Copy Binary Protocol

The `BinaryProtocol` implementation at [BinaryProtocol.swift#L44-L86](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/BitFoundation/Sources/BitFoundation/BinaryProtocol.swift) uses Swift `Data` views rather than buffer copies:

```swift
// From BinaryProtocol.swift - zero-copy view creation
public func readBytes(count: Int) -> Data? {
    guard position + count <= data.count else { return nil }
    let view = data[position..<position+count]  // No allocation, just slice
    position += count
    return Data(view)  // Only copies when explicitly converted
}

```

This design minimizes heap pressure during high-frequency BLE packet processing, keeping garbage collection pauses negligible on mobile devices.

### LZ4 Compression for BLE Payloads

As documented in [README.md#L34](https://github.com/permissionlesstech/bitchat/blob/main/README.md), LZ4 compression reduces payload size before BLE transmission. The compression ratio typically achieves 2-4x for text messages, directly reducing:

- Radio-on time and energy consumption
- Packet fragmentation across BLE MTU limits
- Collision probability in shared mesh spectrum

### Noise Protocol for Mesh Encryption

The `NoiseEncryptionService` at [NoiseEncryptionService.swift#L78](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/NoiseEncryptionService.swift) implements the Noise Protocol Framework for forward-secure, zero-round-trip handshakes. Compared to TLS or naive ECDH, Noise provides:

- Smaller per-message overhead (16 bytes MAC vs. typical TLS record)
- No certificate parsing or validation latency
- Constant-time operations suitable for timing-attack resistance

### Message Deduplication Architecture

Both Nostr and BLE paths share the `MessageDeduplicator` with O(1) key lookup. The deduplicator stores 64-bit SipHash outputs of message IDs, providing collision-resistant duplicate detection with minimal memory footprint. Keys expire based on time-to-live policies coordinated with mesh synchronization windows.

### Adaptive Battery-Aware Scheduling

BitChat implements three power modes controlled by activity heuristics:

| Mode | BLE Scan Interval | Trigger | Use Case |
|------|-------------------|---------|----------|
| **Active** | 30ms | User in conversation | Real-time responsiveness |
| **Background** | 300ms | App backgrounded, recent activity | Opportunistic sync |
| **Dormant** | 3000ms | Extended idle period | Battery preservation |

This adaptation at [README.md#L34](https://github.com/permissionlesstech/bitchat/blob/main/README.md) maintains connectivity without the ~100mW continuous drain of aggressive BLE scanning.

### Single-Source ConversationStore Design

The `ConversationStore` architecture documented in [CONVERSATION-STORE-DESIGN.md](https://github.com/permissionlesstech/bitchat/blob/main/docs/CONVERSATION-STORE-DESIGN.md) eliminates the N+1 indexing problem common in ORM-based messaging apps. All mutations funnel through one ordered array with binary-search insertion:

```swift
// Representative usage pattern from PerformanceBaselineTests
let store = ConversationStore()
store.append(message, to: .mesh)  // O(log n) via binary search on timestamp

```

The retention-capped steady state achieves 2.4x higher throughput than general insertion because eviction appends to the end rather than requiring position search.

## Running and Interpreting Performance Tests

### Local Benchmark Execution

```bash

# Skip performance tests for rapid functional iteration

BITCHAT_SKIP_PERF_BASELINES=1 swift test

# Run full performance suite with logging

BITCHAT_PERF_LOG=perf.log swift test --filter PerformanceBaselineTests

# Parse results

grep "PERF\[" perf.log

```

The `BITCHAT_PERF_LOG` environment variable triggers human-readable throughput reporting. The CI script at [scripts/check-perf-floors.sh](https://github.com/permissionlesstech/bitchat/blob/main/scripts/check-perf-floors.sh) validates these against minimum thresholds to prevent performance regressions.

### Understanding Benchmark Output Format

```

PERF[store.append]: 5400 messages/sec (avg 0.185 ms per pass of 1000, 5 passes)

```

- **5400 messages/sec**: Sustained throughput across 5 measurement passes
- **0.185 ms per pass**: Amortized time for 1,000 append operations
- **5 passes**: Statistical sample count for variance reduction

### Custom Throughput Measurement

```swift
import XCTest
@testable import bitchat

func measureCustomPipeline() {
    let dedup = MessageDeduplicator()
    let packets = (0..<1000).map { _ in generateTestPacket() }
    
    let start = CFAbsoluteTimeGetCurrent()
    for packet in packets {
        let data = packet.toBinaryData()!
        let decoded = BitchatPacket.from(data)!
        _ = dedup.isDuplicate(dedupKey(for: decoded))
    }
    let elapsed = CFAbsoluteTimeGetCurrent() - start
    
    let throughput = Double(packets.count) / elapsed
    print("Custom throughput: \(Int(throughput)) packets/sec")
}

```

This mirrors the methodology in `testBLEInboundPacketPipeline` while allowing domain-specific extensions.

## Key Source Files for Performance Analysis

| File | Purpose | Performance Relevance |
|------|---------|----------------------|
| [`PerformanceBaselineTests.swift`](https://github.com/permissionlesstech/bitchat/blob/main/PerformanceBaselineTests.swift) | Micro-benchmark suite defining throughput floors | Source of all quantitative claims in this analysis |
| [`BinaryProtocol.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BinaryProtocol.swift) | Zero-copy encoding/decoding | Enables 11k packets/sec BLE processing |
| [`NoiseEncryptionService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/NoiseEncryptionService.swift) | Fast cryptographic operations | Sub-millisecond per-message overhead |
| [`ConversationStore.swift`](https://github.com/permissionlesstech/bitchat/blob/main/ConversationStore.swift) | Ordered message storage | O(log n) insertion with retention management |
| [`MessageDeduplicator.swift`](https://github.com/permissionlesstech/bitchat/blob/main/MessageDeduplicator.swift) | O(1) duplicate detection | Powers 30k events/sec fast path |
| [`GCSFilter.swift`](https://github.com/permissionlesstech/bitchat/blob/main/GCSFilter.swift) | Bandwidth-efficient sync | 140 filters/sec build/decode |
| [`ChatViewModel.swift`](https://github.com/permissionlesstech/bitchat/blob/main/ChatViewModel.swift) | Pipeline orchestration | 900-1,000 messages/sec end-to-end ingest |
| [`scripts/check-perf-floors.sh`](https://github.com/permissionlesstech/bitchat/blob/main/scripts/check-perf-floors.sh) | CI regression prevention | Validates benchmarks against minima |

## Summary

BitChat performance characteristics reflect deliberate architectural tradeoffs for dual-transport messaging:

- **Nostr path**: 2,000-30,000 events/second depending on deduplication hit rate, with cryptographic verification as the bottleneck
- **BLE mesh path**: 11,000 packets/second decode/deduplication, enabled by zero-copy binary protocol and fast Noise encryption
- **Storage layer**: 5,000-12,000 messages/second append with retention management, using binary-search ordered insertion
- **Sync optimization**: GCS filters at 140 operations/second for bandwidth-efficient mesh state reconciliation
- **Power efficiency**: Adaptive battery modes scaling from 30ms to 3000ms scan intervals based on activity heuristics

These figures derive from the `PerformanceBaselineTests` suite and represent upper bounds on modern hardware. Mobile devices typically achieve 30-60% of CI-measured throughput due to thermal throttling and background execution limits, still well within interactive latency requirements for real-time messaging.

## Frequently Asked Questions

### What limits BitChat's Nostr ingestion throughput?

Cryptographic verification of fresh events bounds fresh-event processing to ~2,000 events/second. The duplicate path at ~30,000 events/second demonstrates that parsing and deduplication are not bottlenecks—ED25519 signature verification and SHA-256 hashing dominate CPU usage for unseen events.

### How does BitChat maintain performance as conversation history grows?

The `ConversationStore` uses fixed-size retention windows (default 1,337 messages per channel) with O(1) eviction of oldest entries. Steady-state benchmarks show 2.4x higher throughput than general insertion because append-mostly workloads with eviction avoid binary-search positioning costs.

### Can performance benchmarks detect regressions in CI?

Yes. The [`scripts/check-perf-floors.sh`](https://github.com/permissionlesstech/bitchat/blob/main/scripts/check-perf-floors.sh) script parses `BITCHAT_PERF_LOG` output and fails builds when any benchmark drops below established floors. These floors are set at ~80% of observed典型CI performance to account for runner variance while catching meaningful regressions.

### Why is BLE throughput measured without actual radio transmission?

The `testBLEInboundPacketPipeline` benchmark isolates protocol processing from radio variability. Actual mesh performance depends on environmental factors (interference, distance, concurrent transmitters) that would introduce noise into software optimization measurements. The 11,000 packets/second figure represents the CPU-bound ceiling—radio limitations typically reduce effective throughput to 50-200 packets/second in dense urban environments.