ANN-route-predition/pyrate/scripts/benchmark_graph_neighbor_search.py

101 lines
3.9 KiB
Python

#!/usr/bin/env python3
"""Benchmark the neighbor search in graphs.
Initially written as part of
`Issue #90 <https://gitlab.sailingteam.hg.tu-darmstadt.de/informatik/pyrate/-/issues/90>`__ to determine
whether a faster implementation is needed.
That issue also contains a draft which *might* make it faster if that is required in the future.
Examples:
These are the benchmark results when run on a `Lenovo ThinkPad T560 laptop <https://thinkwiki.de/T560>`__
with an `Intel(R) Core(TM) i5-6300U CPU @ 2.40GHz
<https://ark.intel.com/content/www/de/de/ark/products/88190/intel-core-i5-6300u-processor-3m-cache-up-to-3-00-ghz.html>`__
and 16GB RAM (at commit ``9a8177326dc0d82d0aea4559e6c85071ceebf56f``):
.. code-block:: bash
./scripts/benchmark_graph_neighbor_search.py --iterations 100
frequency = 2 for distance 5000 km
generated graph in 0.018192768096923828 seconds
number of nodes = 42, number of edges = 120
non-empty entries in neighbor table = 240
computation time = 0.0003081770000221695 (avg. over 100 samples)
frequency = 8 for distance 1000 km
generated graph in 0.0327601432800293 seconds
number of nodes = 642, number of edges = 1920
non-empty entries in neighbor table = 3840
computation time = 0.0033796210000218707 (avg. over 100 samples)
frequency = 71 for distance 100 km
generated graph in 1.8711962699890137 seconds
number of nodes = 50412, number of edges = 151230
non-empty entries in neighbor table = 302460
computation time = 0.30925760600001695 (avg. over 100 samples)
frequency = 142 for distance 50 km
generated graph in 7.630561828613281 seconds
number of nodes = 201642, number of edges = 604920
non-empty entries in neighbor table = 1209840
computation time = 1.1302456550000102 (avg. over 100 samples)
frequency = 706 for distance 10 km
generated graph in 260.7689461708069 seconds
number of nodes = 4984362, number of edges = 14953080
non-empty entries in neighbor table = 29906160
computation time = 27.382845137000004 (avg. over 100 samples)
"""
# Standard library
from argparse import ArgumentDefaultsHelpFormatter
from argparse import ArgumentParser
from time import time
from timeit import timeit
# Scientific
import numpy
# Graph
from pyrate.plan.graph.generate import create_earth_graph
from pyrate.plan.graph.generate import min_required_frequency
from pyrate.plan.graph import NavigationGraph
def _main() -> None:
"""The main function."""
parser = ArgumentParser(
description="Benchmark the neighbor search in graphs.", formatter_class=ArgumentDefaultsHelpFormatter
)
parser.add_argument(
"--iterations", type=int, default=100, help="the number of timing samples to collect per graph size"
)
args = parser.parse_args()
for distance_km in [5000, 1000, 100, 50, 10]:
frequency = min_required_frequency(distance_km * 1000, in_meters=True)
print(f"frequency = {frequency} for distance {distance_km} km")
time_before_generation = time()
graph = create_earth_graph(frequency, print_status=False)
print(f"generated graph in {time() - time_before_generation} seconds")
print(f"number of nodes = {len(graph)}, number of edges = {graph.num_edges}")
def setup(local_graph: NavigationGraph = graph) -> None:
local_graph._neighbors = None # pylint: disable=protected-access
def statement(local_graph: NavigationGraph = graph) -> None:
_ = local_graph.neighbors
avg_time = timeit(setup=setup, stmt=statement, number=args.iterations)
print(f"non-empty entries in neighbor table = {numpy.count_nonzero(graph.neighbors != -1)}")
print(f"computation time = {avg_time} (avg. over {args.iterations} samples)")
print() # empty line between distances
if __name__ == "__main__":
_main()