Perf Protocol Demo
This example demonstrates how to use the libp2p perf protocol to measure
transfer performance between two nodes.
The perf protocol sends and receives data to measure throughput, reporting both intermediary progress and final results.
Running the Example
First, start the server in one terminal:
$ python examples/perf/perf_example.py -p 8000
Perf server ready, listening on:
/ip4/127.0.0.1/tcp/8000/p2p/QmXfptdHU6hqG95JswxYVUH4bphcK8y18mhFcgUQFe6fCN
Protocol: /perf/1.0.0
Run client with:
python perf_example.py -d /ip4/127.0.0.1/tcp/8000/p2p/QmXfptdHU6hqG95JswxYVUH4bphcK8y18mhFcgUQFe6fCN
Waiting for incoming perf requests...
Then, in another terminal, run the client with the multiaddr from the server:
$ python examples/perf/perf_example.py -d /ip4/127.0.0.1/tcp/8000/p2p/QmXfptdHU6hqG95JswxYVUH4bphcK8y18mhFcgUQFe6fCN
Connecting to QmXfptdHU6hqG95JswxYVUH4bphcK8y18mhFcgUQFe6fCN...
Connected!
Measuring performance:
Upload: 2560 bytes
Download: 2560 bytes
Uploading: 2560 bytes in 0.01s (256000 bytes/s)
Downloading: 2560 bytes in 0.01s (256000 bytes/s)
==================================================
Performance Results:
Total time: 0.025 seconds
Uploaded: 2560 bytes
Downloaded: 2560 bytes
Total data: 5120 bytes
Throughput: 204800 bytes/s
==================================================
Command Line Options
-p, --port Listening port (default: random free port)
-d, --destination Destination multiaddr (if not set, runs as server)
-u, --upload Upload size in units of 256 bytes (default: 10)
-D, --download Download size in units of 256 bytes (default: 10)
Source Code
1"""
2Perf protocol example - Measure transfer performance between two libp2p nodes.
3
4Usage:
5 # Terminal 1 - Run the server (listener)
6 python perf_example.py -p 8000
7
8 # Terminal 2 - Run the client (measures performance to server)
9 python perf_example.py -p 8001 -d /ip4/127.0.0.1/tcp/8000/p2p/<PEER_ID>
10"""
11
12import argparse
13
14import multiaddr
15import trio
16
17from libp2p import new_host
18from libp2p.peer.peerinfo import info_from_p2p_addr
19from libp2p.perf import PROTOCOL_NAME, PerfService
20
21ONE_UNIT = 16 * 16 # 256 bytes
22UPLOAD_BYTES = ONE_UNIT * 10 # 2560 bytes upload
23DOWNLOAD_BYTES = ONE_UNIT * 10 # 2560 bytes download
24
25
26async def run_server(host, perf_service) -> None:
27 """Run as a perf server - listens for incoming perf requests."""
28 await perf_service.start()
29
30 print("\nPerf server ready, listening on:")
31 for addr in host.get_addrs():
32 print(f" {addr}")
33
34 print(f"\nProtocol: {PROTOCOL_NAME}")
35 print("\nRun client with:")
36 print(f" python perf_example.py -d {host.get_addrs()[0]}")
37 print("\nWaiting for incoming perf requests...")
38
39 await trio.sleep_forever()
40
41
42async def run_client(
43 host, perf_service, destination: str, upload_bytes: int, download_bytes: int
44) -> None:
45 """Run as a perf client - measures performance to a remote peer."""
46 await perf_service.start()
47
48 maddr = multiaddr.Multiaddr(destination)
49 info = info_from_p2p_addr(maddr)
50
51 print(f"\nConnecting to {info.peer_id}...")
52 await host.connect(info)
53 print("Connected!")
54
55 print("\nMeasuring performance:")
56 print(f" Upload: {upload_bytes} bytes")
57 print(f" Download: {download_bytes} bytes")
58 print()
59
60 async for output in perf_service.measure_performance(
61 maddr, upload_bytes, download_bytes
62 ):
63 if output["type"] == "intermediary":
64 # Progress report
65 upload_bytes_out = output["upload_bytes"]
66 download_bytes_out = output["download_bytes"]
67 time_s = output["time_seconds"]
68
69 if upload_bytes_out > 0:
70 throughput = upload_bytes_out / time_s if time_s > 0 else 0
71 print(
72 f" Upload: {upload_bytes_out}B in {time_s:.2f}s "
73 f"({throughput:.0f} B/s)"
74 )
75 elif download_bytes_out > 0:
76 throughput = download_bytes_out / time_s if time_s > 0 else 0
77 print(
78 f" Download: {download_bytes_out}B in {time_s:.2f}s "
79 f"({throughput:.0f} B/s)"
80 )
81
82 elif output["type"] == "final":
83 # Final summary
84 total_time = output["time_seconds"]
85 total_upload = output["upload_bytes"]
86 total_download = output["download_bytes"]
87 total_data = total_upload + total_download
88
89 print(f"\n{'=' * 50}")
90 print("Performance Results:")
91 print(f" Total time: {total_time:.3f} seconds")
92 print(f" Uploaded: {total_upload} bytes")
93 print(f" Downloaded: {total_download} bytes")
94 print(f" Total data: {total_data} bytes")
95 print(f" Throughput: {total_data / total_time:.0f} bytes/s")
96 print(f"{'=' * 50}")
97
98 await perf_service.stop()
99
100
101async def run(port: int, destination: str, upload_mb: int, download_mb: int) -> None:
102 """Main run function."""
103 from libp2p.utils.address_validation import find_free_port
104
105 if port <= 0:
106 port = find_free_port()
107
108 listen_addrs = [multiaddr.Multiaddr(f"/ip4/127.0.0.1/tcp/{port}")]
109 host = new_host(listen_addrs=listen_addrs)
110
111 # Create perf service
112 perf_service = PerfService(host)
113
114 async with host.run(listen_addrs=listen_addrs):
115 if destination:
116 # Client mode
117 await run_client(
118 host,
119 perf_service,
120 destination,
121 upload_mb * ONE_UNIT,
122 download_mb * ONE_UNIT,
123 )
124 else:
125 # Server mode
126 await run_server(host, perf_service)
127
128
129def main() -> None:
130 description = """
131 Perf protocol example - Measure transfer performance between libp2p nodes.
132
133 To use:
134 1. Start server: python perf_example.py -p 8000
135 2. Start client: python perf_example.py -d <MULTIADDR_FROM_SERVER>
136 """
137
138 parser = argparse.ArgumentParser(description=description)
139 parser.add_argument("-p", "--port", default=0, type=int, help="listening port")
140 parser.add_argument("-d", "--destination", type=str, help="destination multiaddr")
141 parser.add_argument(
142 "-u",
143 "--upload",
144 default=10,
145 type=int,
146 help="upload size in units of 256 bytes (default: 10)",
147 )
148 parser.add_argument(
149 "-D",
150 "--download",
151 default=10,
152 type=int,
153 help="download size in units of 256 bytes (default: 10)",
154 )
155
156 args = parser.parse_args()
157
158 try:
159 trio.run(run, args.port, args.destination, args.upload, args.download)
160 except KeyboardInterrupt:
161 print("\nShutting down...")
162
163
164if __name__ == "__main__":
165 main()