150 lines
5.4 KiB
Python
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()
|