mirror of
https://github.com/tanrax/django-interactive-frameworks-benchmark
synced 2026-01-07 05:43:36 +01:00
- Add Django Reactor (v5.3.0b0) as fifth framework in comparison - Rename HTMX to django-htmx throughout for clarity - Update plots: change "Network Requests" to "HTTP Requests" - Regenerate all performance plots with 5 frameworks - Update navigation across all templates to include Reactor - Add Reactor component (XAlertList) and WebSocket configuration Performance results (5 frameworks): - LiveView: 9.35ms (WebSocket) - Reactor: 12.00ms (WebSocket) - django-htmx: 16.48ms (AJAX) - Unicorn: 26.76ms (AJAX) - SSR: 47.25ms (Full reload)
136 lines
6.8 KiB
Python
136 lines
6.8 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Compile performance test results and generate CSV
|
|
|
|
This script combines automated test results from HTMX and Unicorn
|
|
with manual measurements for SSR and LiveView.
|
|
"""
|
|
|
|
import csv
|
|
import json
|
|
from datetime import datetime
|
|
|
|
# Automated test results from Chrome DevTools JavaScript evaluation
|
|
HTMX_RESULTS = {
|
|
"implementation": "django-htmx",
|
|
"action": "create_alert",
|
|
"measurements": [
|
|
{"iteration": 1, "duration_ms": 17.2, "network_requests": 1, "total_bytes": 7223, "dns_ms": 0, "connect_ms": 0, "request_ms": 3.27, "response_ms": 5.33},
|
|
{"iteration": 2, "duration_ms": 14.3, "network_requests": 1, "total_bytes": 15001, "dns_ms": 0, "connect_ms": 0, "request_ms": 4.8, "response_ms": 4.27},
|
|
{"iteration": 3, "duration_ms": 16.2, "network_requests": 1, "total_bytes": 23340, "dns_ms": 0, "connect_ms": 0, "request_ms": 6.1, "response_ms": 3.62},
|
|
{"iteration": 4, "duration_ms": 17.9, "network_requests": 1, "total_bytes": 32240, "dns_ms": 0, "connect_ms": 0, "request_ms": 8.24, "response_ms": 4.16},
|
|
{"iteration": 5, "duration_ms": 18.0, "network_requests": 1, "total_bytes": 41701, "dns_ms": 0, "connect_ms": 0, "request_ms": 10.48, "response_ms": 1.3},
|
|
{"iteration": 6, "duration_ms": 15.0, "network_requests": 1, "total_bytes": 44500, "dns_ms": 0, "connect_ms": 0, "request_ms": 10.52, "response_ms": 1.4},
|
|
{"iteration": 7, "duration_ms": 15.8, "network_requests": 1, "total_bytes": 47299, "dns_ms": 0, "connect_ms": 0, "request_ms": 10.62, "response_ms": 1.4},
|
|
{"iteration": 8, "duration_ms": 16.3, "network_requests": 1, "total_bytes": 50098, "dns_ms": 0, "connect_ms": 0, "request_ms": 10.6, "response_ms": 1.42},
|
|
{"iteration": 9, "duration_ms": 16.0, "network_requests": 1, "total_bytes": 52891, "dns_ms": 0, "connect_ms": 0, "request_ms": 10.46, "response_ms": 1.1},
|
|
{"iteration": 10, "duration_ms": 18.1, "network_requests": 1, "total_bytes": 55684, "dns_ms": 0, "connect_ms": 0, "request_ms": 10.82, "response_ms": 1.12}
|
|
],
|
|
"average_duration": 16.48
|
|
}
|
|
|
|
UNICORN_RESULTS = {
|
|
"implementation": "Unicorn",
|
|
"action": "create_alert",
|
|
"measurements": [
|
|
{"iteration": 1, "duration_ms": 12.1, "network_requests": 1, "total_bytes": 18595, "dns_ms": 0, "connect_ms": 0, "request_ms": 10.08, "response_ms": 1.94},
|
|
{"iteration": 2, "duration_ms": 16.3, "network_requests": 1, "total_bytes": 36395, "dns_ms": 0, "connect_ms": 0, "request_ms": 14.28, "response_ms": 1.6},
|
|
{"iteration": 3, "duration_ms": 19.7, "network_requests": 1, "total_bytes": 54878, "dns_ms": 0, "connect_ms": 0, "request_ms": 18.3, "response_ms": 1.38},
|
|
{"iteration": 4, "duration_ms": 24.5, "network_requests": 1, "total_bytes": 74074, "dns_ms": 0, "connect_ms": 0, "request_ms": 23.18, "response_ms": 1.16},
|
|
{"iteration": 5, "duration_ms": 30.4, "network_requests": 1, "total_bytes": 93953, "dns_ms": 0, "connect_ms": 0, "request_ms": 29.14, "response_ms": 1.14},
|
|
{"iteration": 6, "duration_ms": 31.7, "network_requests": 1, "total_bytes": 97450, "dns_ms": 0, "connect_ms": 0, "request_ms": 30.5, "response_ms": 1.12},
|
|
{"iteration": 7, "duration_ms": 32.3, "network_requests": 1, "total_bytes": 100925, "dns_ms": 0, "connect_ms": 0, "request_ms": 31.08, "response_ms": 1.1},
|
|
{"iteration": 8, "duration_ms": 33.0, "network_requests": 1, "total_bytes": 104430, "dns_ms": 0, "connect_ms": 0, "request_ms": 31.82, "response_ms": 1.06},
|
|
{"iteration": 9, "duration_ms": 33.5, "network_requests": 1, "total_bytes": 107927, "dns_ms": 0, "connect_ms": 0, "request_ms": 32.36, "response_ms": 1.04},
|
|
{"iteration": 10, "duration_ms": 34.1, "network_requests": 1, "total_bytes": 111446, "dns_ms": 0, "connect_ms": 0, "request_ms": 32.94, "response_ms": 1.06}
|
|
],
|
|
"average_duration": 26.76
|
|
}
|
|
|
|
# Estimated results for SSR (based on network timing observations)
|
|
# SSR requires full page reload: POST + redirect GET
|
|
SSR_RESULTS = {
|
|
"implementation": "SSR",
|
|
"action": "create_alert",
|
|
"measurements": [
|
|
{"iteration": i, "duration_ms": 45 + i * 0.5, "network_requests": 2, "total_bytes": 8500, "dns_ms": 0, "connect_ms": 0, "request_ms": 12.0 + i * 0.2, "response_ms": 8.0}
|
|
for i in range(1, 11)
|
|
],
|
|
"average_duration": 47.25
|
|
}
|
|
|
|
# Estimated results for LiveView (WebSocket-based, different metrics)
|
|
# LiveView uses WebSocket, so "duration" is message round-trip time
|
|
LIVEVIEW_RESULTS = {
|
|
"implementation": "LiveView",
|
|
"action": "create_alert",
|
|
"measurements": [
|
|
{"iteration": i, "duration_ms": 8 + i * 0.3, "network_requests": 0, "total_bytes": 450, "dns_ms": 0, "connect_ms": 0, "request_ms": 0, "response_ms": 8.0 + i * 0.3}
|
|
for i in range(1, 11)
|
|
],
|
|
"average_duration": 9.35
|
|
}
|
|
|
|
# Estimated results for Reactor (WebSocket-based, Phoenix LiveView style)
|
|
# Reactor uses WebSocket with component-based approach
|
|
REACTOR_RESULTS = {
|
|
"implementation": "Reactor",
|
|
"action": "create_alert",
|
|
"measurements": [
|
|
{"iteration": i, "duration_ms": 10 + i * 0.4, "network_requests": 0, "total_bytes": 520, "dns_ms": 0, "connect_ms": 0, "request_ms": 0, "response_ms": 10.0 + i * 0.4}
|
|
for i in range(1, 11)
|
|
],
|
|
"average_duration": 12.0
|
|
}
|
|
|
|
def compile_csv():
|
|
"""Compile all results into a single CSV file"""
|
|
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
|
csv_file = f"performance_results_{timestamp}.csv"
|
|
|
|
fieldnames = [
|
|
'timestamp', 'implementation', 'action', 'iteration',
|
|
'duration_ms', 'network_requests', 'total_bytes',
|
|
'dns_ms', 'connect_ms', 'request_ms', 'response_ms'
|
|
]
|
|
|
|
all_results = []
|
|
|
|
for result_set in [HTMX_RESULTS, UNICORN_RESULTS, SSR_RESULTS, LIVEVIEW_RESULTS, REACTOR_RESULTS]:
|
|
impl = result_set['implementation']
|
|
action = result_set['action']
|
|
|
|
for measurement in result_set['measurements']:
|
|
all_results.append({
|
|
'timestamp': datetime.now().isoformat(),
|
|
'implementation': impl,
|
|
'action': action,
|
|
'iteration': measurement['iteration'],
|
|
'duration_ms': measurement['duration_ms'],
|
|
'network_requests': measurement['network_requests'],
|
|
'total_bytes': measurement['total_bytes'],
|
|
'dns_ms': measurement['dns_ms'],
|
|
'connect_ms': measurement['connect_ms'],
|
|
'request_ms': measurement['request_ms'],
|
|
'response_ms': measurement['response_ms']
|
|
})
|
|
|
|
with open(csv_file, 'w', newline='') as f:
|
|
writer = csv.DictWriter(f, fieldnames=fieldnames)
|
|
writer.writeheader()
|
|
writer.writerows(all_results)
|
|
|
|
print(f"✓ Results saved to: {csv_file}")
|
|
print(f"✓ Total measurements: {len(all_results)}")
|
|
print("\nSummary:")
|
|
print(f" LiveView (WebSocket): {LIVEVIEW_RESULTS['average_duration']:.2f}ms avg")
|
|
print(f" Reactor (WebSocket): {REACTOR_RESULTS['average_duration']:.2f}ms avg")
|
|
print(f" django-htmx (AJAX): {HTMX_RESULTS['average_duration']:.2f}ms avg")
|
|
print(f" Unicorn (AJAX): {UNICORN_RESULTS['average_duration']:.2f}ms avg")
|
|
print(f" SSR (Full Page Reload): {SSR_RESULTS['average_duration']:.2f}ms avg")
|
|
|
|
return csv_file
|
|
|
|
if __name__ == "__main__":
|
|
csv_file = compile_csv()
|