Performance

C extension

During installation timezonefinder automatically tries to compile a C extension with an implementation of the time critical point in polygon check algorithm. In order for this to work, a Clang compiler has to be installed.

Note

If compilation fails (due to e.g. missing C compiler or broken cffi installation) timezonefinder will silently fall back to a pure Python implementation (~400x slower, cf. speed test results below).

For testing if the compiled C implementation of the point in polygon algorithm is being used:

TimezoneFinder.using_clang_pip()  # returns True or False

Numba

Some of the utility function (cf. utils.py) can be JIT compiled automatically by installing the optional dependency numba:

pip install timezonefinder[numba]

It is highly recommended to install Numba for increased performance when the C extensions cannot be used (e.g. C compiler is not present at build time).

Note

If Numba can be imported, the JIT compiled Python version of the point in polygon check algorithm will be used instead of the C alternative as it is even faster (cf. speed test results below).

For testing if Numba is being used to JIT compile helper functions:

TimezoneFinder.using_numba()  # returns True or False

In memory mode

To speed up the computations at the cost of memory consumption and initialisation time, pass in_memory=True during initialisation. This causes all binary files to be read into memory.

tf = TimezoneFinder(in_memory=True)

Speed Benchmark Results

obtained on MacBook Pro (15-inch, 2017), 2,8 GHz Intel Core i7 and timezonefinder version 6.1.0

Timezone finding

scripts/check_speed_timezone_finding.py

Without Numba (using C extension):

using C implementation: True
using Numba: False

10000 'on land points' (points included in a land timezone)
in memory mode: False

function name                       | s/query    | pts/s
--------------------------------------------------
TimezoneFinder.timezone_at()        | 7.5e-05    | 1.3e+04
TimezoneFinder.timezone_at_land()   | 7.7e-05    | 1.3e+04
TimezoneFinderL.timezone_at()       | 7.3e-06    | 1.4e+05
TimezoneFinderL.timezone_at_land()  | 8.3e-06    | 1.2e+05

10000 random points (anywhere on earth)
in memory mode: False

function name                       | s/query    | pts/s
--------------------------------------------------
TimezoneFinder.timezone_at()        | 8.8e-05    | 1.1e+04
TimezoneFinder.timezone_at_land()   | 8.9e-05    | 1.1e+04
TimezoneFinderL.timezone_at()       | 6.6e-06    | 1.5e+05
TimezoneFinderL.timezone_at_land()  | 9.5e-06    | 1.1e+05

10000 'on land points' (points included in a land timezone)
in memory mode: True

function name                       | s/query    | pts/s
--------------------------------------------------
TimezoneFinder.timezone_at()        | 3.9e-05    | 2.6e+04
TimezoneFinder.timezone_at_land()   | 4.0e-05    | 2.5e+04
TimezoneFinderL.timezone_at()       | 6.3e-06    | 1.6e+05
TimezoneFinderL.timezone_at_land()  | 8.6e-06    | 1.2e+05

10000 random points (anywhere on earth)
in memory mode: True

function name                       | s/query    | pts/s
--------------------------------------------------
TimezoneFinder.timezone_at()        | 3.5e-05    | 2.8e+04
TimezoneFinder.timezone_at_land()   | 3.9e-05    | 2.6e+04
TimezoneFinderL.timezone_at()       | 6.9e-06    | 1.5e+05
TimezoneFinderL.timezone_at_land()  | 9.0e-06    | 1.1e+05

With Numba:

using C implementation: False
using Numba: True

10000 'on land points' (points included in a land timezone)
in memory mode: False

function name                       | s/query    | pts/s
--------------------------------------------------
TimezoneFinder.timezone_at()        | 7.1e-05    | 1.4e+04
TimezoneFinder.timezone_at_land()   | 7.4e-05    | 1.3e+04
TimezoneFinderL.timezone_at()       | 6.5e-06    | 1.5e+05
TimezoneFinderL.timezone_at_land()  | 9.1e-06    | 1.1e+05

10000 random points (anywhere on earth)
in memory mode: False

function name                       | s/query    | pts/s
--------------------------------------------------
TimezoneFinder.timezone_at()        | 8.2e-05    | 1.2e+04
TimezoneFinder.timezone_at_land()   | 8.1e-05    | 1.2e+04
TimezoneFinderL.timezone_at()       | 6.9e-06    | 1.5e+05
TimezoneFinderL.timezone_at_land()  | 8.8e-06    | 1.1e+05

10000 'on land points' (points included in a land timezone)
in memory mode: True

function name                       | s/query    | pts/s
--------------------------------------------------
TimezoneFinder.timezone_at()        | 3.7e-05    | 2.7e+04
TimezoneFinder.timezone_at_land()   | 4.0e-05    | 2.5e+04
TimezoneFinderL.timezone_at()       | 6.9e-06    | 1.5e+05
TimezoneFinderL.timezone_at_land()  | 8.1e-06    | 1.2e+05

10000 random points (anywhere on earth)
in memory mode: True

function name                       | s/query    | pts/s
--------------------------------------------------
TimezoneFinder.timezone_at()        | 3.2e-05    | 3.1e+04
TimezoneFinder.timezone_at_land()   | 3.4e-05    | 2.9e+04
TimezoneFinderL.timezone_at()       | 6.4e-06    | 1.6e+05
TimezoneFinderL.timezone_at_land()  | 7.6e-06    | 1.3e+05

Point in polygon checks

scripts/check_speed_inside_polygon.py

Without Numba:

testing the speed of the different point in polygon algorithm implementations
testing 1000 queries: random points and timezone polygons
Python implementation using Numba JIT compilation: False

inside_clang: 2.7e-05 s/query, 3.7e+04 queries/s
inside_python: 1.0e-02 s/query, 9.9e+01 queries/s
C implementation is 374.1x faster than the Python implementation WITHOUT Numba

With Numba:

testing the speed of the different point in polygon algorithm implementations
testing 10000 queries: random points and timezone polygons
Python implementation using Numba JIT compilation: True

inside_clang: 2.2e-05 s/query, 4.5e+04 queries/s
inside_python: 1.8e-05 s/query, 5.5e+04 queries/s
Python implementation WITH Numba is 0.2x faster than the C implementation

Initialisation

testing initialiation: TimezoneFinder(in_memory=True)
avg. startup time: 7.01e-01 (10 runs)

testing initialiation: TimezoneFinder(in_memory=False)
avg. startup time: 7.85e-01 (10 runs)

testing initialiation: TimezoneFinderL(in_memory=True)
avg. startup time: 6.66e-01 (10 runs)

testing initialiation: TimezoneFinderL(in_memory=False)
avg. startup time: 7.30e-01 (10 runs)