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

150 lines
5.4 KiB
Python

#!/usr/bin/env python3
"""This scripts benchmarks both the database reading accesses as well as the
projections between polar and cartesian representations.
The function :func:`_query_database` queries the database for some location and radius which were deemed a
realistic load scenario.
The function :func:`_project_to_cartesian_and_back` takes the result of such a query and projects it to the
cartesian representation and back.
Keep in mind, that these operations might only be performed very seldom on an actual Atlantic crossing
(maybe every couple of hours).
The results and details are now included in :ref:`benchmarking-db-and-local-projections`
(in the documentation of the :mod:`pyrate.plan` module).
This script was initially developed as part of
`issue #40 <https://gitlab.sailingteam.hg.tu-darmstadt.de/informatik/pyrate/-/issues/40>`__,
in order to evaluate whether "custom" local projections are a feasible option on a Raspberry Pi 3B/4B.
Since then, the implementation has changed.
In particular, the database creation as been moved into a separate script
(see :ref:`script-s57_charts_to_db`).
"""
# Standard library
from argparse import ArgumentDefaultsHelpFormatter
from argparse import ArgumentParser
from time import perf_counter
# Typing
from typing import Any
from typing import Callable
from typing import List
# Data modeling
import numpy
# Geospatial
from pyrate.plan.geometry import PolarGeometry
from pyrate.plan.geometry import PolarLocation
# Database
from pyrate.common.charts import SpatialiteDatabase
#: Around Miami, Florida, US.
#: The point was chosen simply because charts are available nearby.
QUERY_AROUND = PolarLocation(longitude=-80.10955810546875, latitude=25.851808634972723)
def _query_database(path_to_db: str, around: PolarLocation, radius: float) -> List[PolarGeometry]:
"""Queries some polygons from the database.
Args:
path_to_db: The path to the database
around: The location around which to query for chart objects
radius: The radius within which to query for chart objects in meters
Returns:
The resulting polygons
"""
with SpatialiteDatabase(path_to_db) as database:
return list(database.read_geometries_around(around=around, radius=radius))
def _project_to_cartesian_and_back(data: List[PolarGeometry]) -> None:
"""Projects some PolarPolygons to their cartesian representation and back to test the performance.
Args:
data: Some polygons to project
"""
assert data # non-emptiness
first = data[0]
center = first if isinstance(first, PolarLocation) else first.locations[0]
for polygon in data:
polygon.to_cartesian(center).to_polar()
def _measure_func(func: Callable[..., Any], name: str, iterations: int, *params, **kw_params) -> None:
"""Measures and prints the running time of a given method.
Args:
func: The callable to execute
name: The name to use for printing
iterations: The number of iterations to average over
*params: Positional arguments to be passed to the callable
**kw_params: Keyword arguments to be passed to the callable
"""
results = numpy.empty((iterations,))
for i in range(iterations):
start = perf_counter()
func(*params, **kw_params)
end = perf_counter()
results[i] = end - start
print(f'Executed "{name}" {iterations} times:')
print(f"\taverage:\t {numpy.mean(results):.6f} seconds")
print(f"\tstd dev:\t {numpy.std(results):.6f} seconds")
print(f"\tvariance:\t {numpy.var(results):.6f} seconds")
def benchmark(path_to_db: str, iterations: int, around: PolarLocation, radius: float) -> None:
"""Performs the benchmark and prints the results to the console.
Args:
path_to_db: The path to the database
iterations: The number of iterations to average over
around: The location around which to query for chart objects
radius: The radius within which to query for chart objects in meters
"""
print("Information on the setting:")
with SpatialiteDatabase(path_to_db) as database:
print(f"\tnumber of rows/polygons in database:\t\t\t {len(database)}")
print(f"\tsum of vertices of all rows/polygons of in database:\t {database.count_vertices()}")
data = _query_database(path_to_db, around, radius)
print(f"\textracted number of polygons:\t\t\t\t {len(data)}")
vertex_count = sum(1 if isinstance(poly, PolarLocation) else len(poly.locations) for poly in data)
print(f"\textracted total number of vertices:\t\t\t {vertex_count}")
print() # newline
_measure_func(_query_database, "query_database", iterations, path_to_db, around, radius)
print() # newline
_measure_func(_project_to_cartesian_and_back, "project_to_cartesian_and_back", iterations, data)
def _main() -> None:
"""The main function."""
parser = ArgumentParser(
description="Benchmark DB queries and projections for a fixed location (see docs/scripts).",
formatter_class=ArgumentDefaultsHelpFormatter,
)
parser.add_argument("path_to_db", type=str)
parser.add_argument("--iterations", type=int, default=10)
parser.add_argument("--radius", type=float, default=100, help="The query radius in kilometers")
args = parser.parse_args()
benchmark(
path_to_db=args.path_to_db, iterations=args.iterations, around=QUERY_AROUND, radius=args.radius * 1000
)
if __name__ == "__main__":
_main()