mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-21 16:14:23 +01:00
pytest: Add benchmarks for direct payments
Add two simple tests: one for a single direct payment and one with hundreds of parallel payments, reusing the same route. Signed-off-by: Christian Decker <decker.christian@gmail.com>
This commit is contained in:
committed by
Rusty Russell
parent
4312deddfa
commit
25725c0aa4
87
tests/benchmark.py
Normal file
87
tests/benchmark.py
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
from lightning import LightningRpc
|
||||||
|
from test_lightningd import NodeFactory
|
||||||
|
import logging
|
||||||
|
import pytest
|
||||||
|
import random
|
||||||
|
import utils
|
||||||
|
|
||||||
|
from concurrent import futures
|
||||||
|
from time import time
|
||||||
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
|
||||||
|
num_workers = 480
|
||||||
|
num_payments = 10000
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def executor():
|
||||||
|
ex = futures.ThreadPoolExecutor(max_workers=num_workers)
|
||||||
|
yield ex
|
||||||
|
ex.shutdown(wait=False)
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def bitcoind():
|
||||||
|
bitcoind = utils.BitcoinD(rpcport=28332)
|
||||||
|
bitcoind.start()
|
||||||
|
info = bitcoind.rpc.getinfo()
|
||||||
|
# Make sure we have segwit and some funds
|
||||||
|
if info['blocks'] < 432:
|
||||||
|
logging.debug("SegWit not active, generating some more blocks")
|
||||||
|
bitcoind.rpc.generate(432 - info['blocks'])
|
||||||
|
elif info['balance'] < 1:
|
||||||
|
logging.debug("Insufficient balance, generating 1 block")
|
||||||
|
bitcoind.rpc.generate(1)
|
||||||
|
|
||||||
|
yield bitcoind
|
||||||
|
|
||||||
|
try:
|
||||||
|
bitcoind.rpc.stop()
|
||||||
|
except:
|
||||||
|
bitcoind.proc.kill()
|
||||||
|
bitcoind.proc.wait()
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def node_factory(request, bitcoind, executor):
|
||||||
|
nf = NodeFactory(request.node.name, bitcoind, executor)
|
||||||
|
yield nf
|
||||||
|
nf.killall()
|
||||||
|
|
||||||
|
def test_single_hop(node_factory, executor):
|
||||||
|
l1 = node_factory.get_node()
|
||||||
|
l2 = node_factory.get_node()
|
||||||
|
|
||||||
|
l1.rpc.connect(l2.rpc.getinfo()['id'], 'localhost:%d' % l2.rpc.getinfo()['port'])
|
||||||
|
l1.openchannel(l2, 4000000)
|
||||||
|
|
||||||
|
print("Collecting invoices")
|
||||||
|
fs = []
|
||||||
|
invoices = []
|
||||||
|
for i in tqdm(range(num_payments)):
|
||||||
|
invoices.append(l2.rpc.invoice(1000, 'invoice-%d' % (i), 'desc')['rhash'])
|
||||||
|
|
||||||
|
route = l1.rpc.getroute(l2.rpc.getinfo()['id'], 1000, 1)['route']
|
||||||
|
print("Sending payments")
|
||||||
|
start_time = time()
|
||||||
|
|
||||||
|
for i in invoices:
|
||||||
|
fs.append(executor.submit(l1.rpc.sendpay, route, i))
|
||||||
|
|
||||||
|
for f in tqdm(fs):
|
||||||
|
f.result()
|
||||||
|
|
||||||
|
diff = time() - start_time
|
||||||
|
print("Done. %d payments performed in %f seconds (%f payments per second)" % (num_payments, diff, num_payments / diff))
|
||||||
|
|
||||||
|
def test_single_payment(node_factory, benchmark):
|
||||||
|
l1 = node_factory.get_node()
|
||||||
|
l2 = node_factory.get_node()
|
||||||
|
l1.rpc.connect(l2.rpc.getinfo()['id'], 'localhost:%d' % l2.rpc.getinfo()['port'])
|
||||||
|
l1.openchannel(l2, 4000000)
|
||||||
|
|
||||||
|
def do_pay(l1, l2):
|
||||||
|
invoice = l2.rpc.invoice(1000, 'invoice-{}'.format(random.random()), 'desc')['bolt11']
|
||||||
|
l1.rpc.pay(invoice)
|
||||||
|
|
||||||
|
benchmark(do_pay, l1, l2)
|
||||||
@@ -84,22 +84,23 @@ def breakpoint():
|
|||||||
class NodeFactory(object):
|
class NodeFactory(object):
|
||||||
"""A factory to setup and start `lightningd` daemons.
|
"""A factory to setup and start `lightningd` daemons.
|
||||||
"""
|
"""
|
||||||
def __init__(self, func, executor):
|
def __init__(self, testname, bitcoind, executor):
|
||||||
self.func = func
|
self.testname = testname
|
||||||
self.next_id = 1
|
self.next_id = 1
|
||||||
self.nodes = []
|
self.nodes = []
|
||||||
self.executor = executor
|
self.executor = executor
|
||||||
|
self.bitcoind = bitcoind
|
||||||
|
|
||||||
def get_node(self, disconnect=None, options=None, may_fail=False):
|
def get_node(self, disconnect=None, options=None, may_fail=False):
|
||||||
node_id = self.next_id
|
node_id = self.next_id
|
||||||
self.next_id += 1
|
self.next_id += 1
|
||||||
|
|
||||||
lightning_dir = os.path.join(
|
lightning_dir = os.path.join(
|
||||||
TEST_DIR, self.func._testMethodName, "lightning-{}/".format(node_id))
|
TEST_DIR, self.testname, "lightning-{}/".format(node_id))
|
||||||
|
|
||||||
socket_path = os.path.join(lightning_dir, "lightning-rpc").format(node_id)
|
socket_path = os.path.join(lightning_dir, "lightning-rpc").format(node_id)
|
||||||
port = 16330+node_id
|
port = 16330+node_id
|
||||||
daemon = utils.LightningD(lightning_dir, bitcoind.bitcoin_dir, port=port)
|
daemon = utils.LightningD(lightning_dir, self.bitcoind.bitcoin_dir, port=port)
|
||||||
# If we have a disconnect string, dump it to a file for daemon.
|
# If we have a disconnect string, dump it to a file for daemon.
|
||||||
if disconnect:
|
if disconnect:
|
||||||
with open(os.path.join(lightning_dir, "dev_disconnect"), "w") as f:
|
with open(os.path.join(lightning_dir, "dev_disconnect"), "w") as f:
|
||||||
@@ -114,7 +115,7 @@ class NodeFactory(object):
|
|||||||
daemon.cmd_line.append(opt)
|
daemon.cmd_line.append(opt)
|
||||||
rpc = LightningRpc(socket_path, self.executor)
|
rpc = LightningRpc(socket_path, self.executor)
|
||||||
|
|
||||||
node = utils.LightningNode(daemon, rpc, bitcoind, self.executor, may_fail=may_fail)
|
node = utils.LightningNode(daemon, rpc, self.bitcoind, self.executor, may_fail=may_fail)
|
||||||
self.nodes.append(node)
|
self.nodes.append(node)
|
||||||
if VALGRIND:
|
if VALGRIND:
|
||||||
node.daemon.cmd_line = [
|
node.daemon.cmd_line = [
|
||||||
@@ -149,7 +150,7 @@ class BaseLightningDTests(unittest.TestCase):
|
|||||||
# Most of the executor threads will be waiting for IO, so
|
# Most of the executor threads will be waiting for IO, so
|
||||||
# let's have a few of them
|
# let's have a few of them
|
||||||
self.executor = futures.ThreadPoolExecutor(max_workers=20)
|
self.executor = futures.ThreadPoolExecutor(max_workers=20)
|
||||||
self.node_factory = NodeFactory(self, self.executor)
|
self.node_factory = NodeFactory(self._testMethodName, bitcoind, self.executor)
|
||||||
|
|
||||||
def getValgrindErrors(self, node):
|
def getValgrindErrors(self, node):
|
||||||
for error_file in os.listdir(node.daemon.lightning_dir):
|
for error_file in os.listdir(node.daemon.lightning_dir):
|
||||||
|
|||||||
Reference in New Issue
Block a user