import matplotlib
matplotlib.use('Agg') import matplotlib.pyplot as plt
import numpy as np
payload_bytes = [8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096]
same_thread_ns = [2.4, 9.8, 11.8, 18.8, 23.3, 34.4, 55.9, 88.1, 149.6, 361.6]
cross_thread_ns = [117.3, 112.3, 118.7, 124.5, 139.4, 147.8, 163.1, 191.4, 225.8, 342.0]
cache_line = 64
base_coherence_ns = 133
def disruptor_estimated_ns(size):
total_bytes = size + 8
num_lines = max(1, (total_bytes + cache_line - 1) // cache_line)
extra_lines = max(0, num_lines - 1)
return base_coherence_ns + extra_lines * 12
disruptor_ns = [disruptor_estimated_ns(s) for s in payload_bytes]
copy_overhead_ns = [ct - 96 for ct in cross_thread_ns]
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 7))
ax1.plot(payload_bytes, same_thread_ns, 'o-', color='#2196F3', linewidth=2.5,
markersize=8, label='Photon Ring (same thread)')
ax1.set_xscale('log', base=2)
ax1.set_xlabel('Payload Size (bytes)', fontsize=13)
ax1.set_ylabel('Roundtrip Latency (ns)', fontsize=13)
ax1.set_title('Same-Thread Roundtrip Latency vs Payload Size', fontsize=14, fontweight='bold')
ax1.set_xticks(payload_bytes)
ax1.set_xticklabels([f'{s}B' if s < 1024 else f'{s//1024}KB' for s in payload_bytes],
rotation=45, ha='right')
ax1.grid(True, alpha=0.3)
ax1.legend(fontsize=11)
ax1.axvline(x=56, color='red', linestyle='--', alpha=0.5)
ax1.annotate('56B = 1 cache line\n(with 8B stamp)', xy=(56, max(same_thread_ns)*0.5),
fontsize=9, color='red', ha='right')
ax2.plot(payload_bytes, cross_thread_ns, 'o-', color='#2196F3', linewidth=2.5,
markersize=8, label='Photon Ring (seqlock + memcpy)')
ax2.plot(payload_bytes, disruptor_ns, 's--', color='#FF5722', linewidth=2.5,
markersize=8, label='Disruptor (in-place, modeled)')
for i in range(len(payload_bytes) - 1):
if cross_thread_ns[i] < disruptor_ns[i] and cross_thread_ns[i+1] >= disruptor_ns[i+1]:
x1, x2 = payload_bytes[i], payload_bytes[i+1]
y1_p, y2_p = cross_thread_ns[i], cross_thread_ns[i+1]
y1_d, y2_d = disruptor_ns[i], disruptor_ns[i+1]
t = (y1_d - y1_p) / ((y2_p - y1_p) - (y2_d - y1_d))
cross_x = x1 + t * (x2 - x1)
cross_y = y1_p + t * (y2_p - y1_p)
ax2.axvline(x=cross_x, color='green', linestyle=':', alpha=0.7)
ax2.annotate(f'Crossover ~{int(cross_x)}B',
xy=(cross_x, cross_y), xytext=(cross_x*1.5, cross_y-20),
fontsize=11, fontweight='bold', color='green',
arrowprops=dict(arrowstyle='->', color='green'))
break
ax2.fill_between(payload_bytes, cross_thread_ns, disruptor_ns,
where=[p < d for p, d in zip(cross_thread_ns, disruptor_ns)],
alpha=0.15, color='#2196F3', label='Photon Ring advantage')
ax2.fill_between(payload_bytes, cross_thread_ns, disruptor_ns,
where=[p >= d for p, d in zip(cross_thread_ns, disruptor_ns)],
alpha=0.15, color='#FF5722', label='Disruptor advantage')
ax2.set_xscale('log', base=2)
ax2.set_xlabel('Payload Size (bytes)', fontsize=13)
ax2.set_ylabel('Cross-Thread Roundtrip Latency (ns)', fontsize=13)
ax2.set_title('Cross-Thread Latency: Photon Ring vs Disruptor', fontsize=14, fontweight='bold')
ax2.set_xticks(payload_bytes)
ax2.set_xticklabels([f'{s}B' if s < 1024 else f'{s//1024}KB' for s in payload_bytes],
rotation=45, ha='right')
ax2.grid(True, alpha=0.3)
ax2.legend(fontsize=10, loc='upper left')
plt.tight_layout()
plt.savefig('docs/images/payload-scaling.png', dpi=150, bbox_inches='tight')
print(f'Saved: docs/images/payload-scaling.png')
print('\n## Payload Scaling Results\n')
print('| Payload | Same-Thread | Cross-Thread | Disruptor (est.) | Winner |')
print('|---------|-------------|--------------|------------------|--------|')
for i, size in enumerate(payload_bytes):
s = f'{size}B' if size < 1024 else f'{size//1024}KB'
winner = '**Photon Ring**' if cross_thread_ns[i] < disruptor_ns[i] else '**Disruptor**'
print(f'| {s:>5} | {same_thread_ns[i]:>8.1f} ns | {cross_thread_ns[i]:>9.1f} ns | {disruptor_ns[i]:>13.1f} ns | {winner} |')