diff --git a/tests/benchmark.py b/tests/benchmark.py new file mode 100644 index 000000000..9e14da0df --- /dev/null +++ b/tests/benchmark.py @@ -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) diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index 217fb2721..bb96fac06 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -84,22 +84,23 @@ def breakpoint(): class NodeFactory(object): """A factory to setup and start `lightningd` daemons. """ - def __init__(self, func, executor): - self.func = func + def __init__(self, testname, bitcoind, executor): + self.testname = testname self.next_id = 1 self.nodes = [] self.executor = executor + self.bitcoind = bitcoind def get_node(self, disconnect=None, options=None, may_fail=False): node_id = self.next_id self.next_id += 1 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) 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 disconnect: with open(os.path.join(lightning_dir, "dev_disconnect"), "w") as f: @@ -114,7 +115,7 @@ class NodeFactory(object): daemon.cmd_line.append(opt) 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) if VALGRIND: node.daemon.cmd_line = [ @@ -149,7 +150,7 @@ class BaseLightningDTests(unittest.TestCase): # Most of the executor threads will be waiting for IO, so # let's have a few of them 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): for error_file in os.listdir(node.daemon.lightning_dir):