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()