Skip to main content

A small Python package showcasing speed differences between NumPy's Python and C APIs.

Project description

PyPI PyPI - Wheel PyPI - Python Version GitHub Workflow Status

We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil [1].

numpy-api-bench is a small Python package comparing speed differences between NumPy’s Python and C APIs that also serves as an example project for writing C extension modules that make use of the NumPy C API [2].

Installation

From source

Linux, Mac, and Windows binary wheels have been built from source on Github Actions runners using the excellent cibuildwheel tool, which eases the process of building binary wheels from compiled code for different platforms. cibuildwheel especially helps with building manylinux wheels.

To build locally, you will need numpy>=1.19 and the latest setuptools [3] installed. Your C compiler should be appropriate for your platform, ex. GCC for Linux, MSVC for Windows, but let setuptools do the work.

First, use git clone or download + unzip to get the repo source code and install the requirements with [4]

pip3 install -r install_requires.txt

After you cd into the repository root, you can build the C extensions in-place and install the package files with

make inplace && pip3 install .

If you don’t have or don’t wish to use make, you may instead use

python3 setup.py build_ext --inplace && pip3 install .

From PyPI

64-bit Python 3.6-3.9 binary wheels for Windows, MacOS, manylinux1, and manylinux2010 can be installed from PyPI, with 32-bit wheels for Windows (x86) and Linux (i686) also available. Install with

pip3 install numpy-api-bench

Package contents

The numpy-api-bench package contains a pure Python module and several C extension modules. The pure Python module is npapibench.pyimpl, containing one function that centers and scales to unit variance a numpy.ndarray that is implemented with only one line of numpy-enabled Python code. It is the “benchmark” for the C extension module npapibench.cimpl, which implements a near-identical function by using the NumPy C API. The other C extension modules are part of the npapibench.functimer subpackage, which provides a callable API for timing the execution of a function with optional arguments in a timeit-like fashion [5].

On installation, setuptools will also create an entry point titled npapibench to access the benchmarking code. Just typing the name of the entry point in the terminal should produce the timeit-like output

numpy.ndarray shape (40, 5, 10, 10, 20, 5), size 2000000
pyimpl.stdscale -- 10 loops, best of 5: 31.9 msec per loop
 cimpl.stdscale -- 50 loops, best of 5: 13.6 msec per loop

For usage details, try npapibench --help.

Unit tests

Testing internal functions

The unit testing requirements for a C extension module are rather unique. Although one is writing C code, the resulting shared object built by setuptools is loaded by the Python interpreter, so it easier to test Python-accessible functions by using Python unit testing tools. However, it is likely that the C extension module, which by convention is a single file with all members static except the module initialization function, may contain some internal functions that cannot be accessed directly from Python. So far, there does not seem to be a widely accepted approach to unit testing code in Python C extensions, especially these internal C functions.

For this project, in separate C extension modules, I wrote Python wrappers for the internal functions I wanted to test, providing a C API for other extension modules by using the header file and PyCapsule method described in the official tutorial on writing Python C extensions. Then, I wrote unit tests in Python using the pytest API and simply invoked pytest to collect and run all unit tests, as it produces far better unit test output compared to most C unit testing frameworks and is aware of Python objects. If there were any segmentation faults or need to more closely debug, I would just then invoke gdb on the Python interpreter running pytest [6] with

gdb --args python3 -m pytest

Together, pytest and gdb allowed me to hammer out a significant number of bugs.

For users

To run the unit tests in the package, pytest>=6.0.1 must be installed. If installing the wheel from PyPI, you can install pytest as an optional dependency alongside the package code with

pip3 install numpy-api-bench[tests]

The unit tests are located in npapibench.tests and npapibench.functimer.tests and can be run with

pytest --pyargs npapibench.tests && pytest --pyargs npapibench.functimer.tests

Other desired flags can be passed to pytest before the --pyargs flag.

If building from source, follow the steps in From source but replace the final pip3 install . with

pip3 install .[tests]

The unit tests can be run after cding to the repository root by simply calling pytest.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

numpy-api-bench-0.1.0.tar.gz (34.0 kB view hashes)

Uploaded Source

Built Distributions

numpy_api_bench-0.1.0-cp39-cp39-win_amd64.whl (49.4 kB view hashes)

Uploaded CPython 3.9 Windows x86-64

numpy_api_bench-0.1.0-cp39-cp39-win32.whl (46.8 kB view hashes)

Uploaded CPython 3.9 Windows x86

numpy_api_bench-0.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl (82.8 kB view hashes)

Uploaded CPython 3.9 manylinux: glibc 2.5+ x86-64

numpy_api_bench-0.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (107.0 kB view hashes)

Uploaded CPython 3.9 manylinux: glibc 2.12+ x86-64 manylinux: glibc 2.5+ x86-64

numpy_api_bench-0.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl (80.0 kB view hashes)

Uploaded CPython 3.9 manylinux: glibc 2.5+ i686

numpy_api_bench-0.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl (103.9 kB view hashes)

Uploaded CPython 3.9 manylinux: glibc 2.12+ i686 manylinux: glibc 2.5+ i686

numpy_api_bench-0.1.0-cp39-cp39-macosx_10_9_x86_64.whl (36.3 kB view hashes)

Uploaded CPython 3.9 macOS 10.9+ x86-64

numpy_api_bench-0.1.0-cp38-cp38-win_amd64.whl (49.3 kB view hashes)

Uploaded CPython 3.8 Windows x86-64

numpy_api_bench-0.1.0-cp38-cp38-win32.whl (46.8 kB view hashes)

Uploaded CPython 3.8 Windows x86

numpy_api_bench-0.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl (85.8 kB view hashes)

Uploaded CPython 3.8 manylinux: glibc 2.5+ x86-64

numpy_api_bench-0.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (111.2 kB view hashes)

Uploaded CPython 3.8 manylinux: glibc 2.12+ x86-64 manylinux: glibc 2.5+ x86-64

numpy_api_bench-0.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl (82.8 kB view hashes)

Uploaded CPython 3.8 manylinux: glibc 2.5+ i686

numpy_api_bench-0.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl (107.6 kB view hashes)

Uploaded CPython 3.8 manylinux: glibc 2.12+ i686 manylinux: glibc 2.5+ i686

numpy_api_bench-0.1.0-cp38-cp38-macosx_10_9_x86_64.whl (36.3 kB view hashes)

Uploaded CPython 3.8 macOS 10.9+ x86-64

numpy_api_bench-0.1.0-cp37-cp37m-win_amd64.whl (49.0 kB view hashes)

Uploaded CPython 3.7m Windows x86-64

numpy_api_bench-0.1.0-cp37-cp37m-win32.whl (46.2 kB view hashes)

Uploaded CPython 3.7m Windows x86

numpy_api_bench-0.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl (81.8 kB view hashes)

Uploaded CPython 3.7m manylinux: glibc 2.5+ x86-64

numpy_api_bench-0.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (109.9 kB view hashes)

Uploaded CPython 3.7m manylinux: glibc 2.12+ x86-64 manylinux: glibc 2.5+ x86-64

numpy_api_bench-0.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl (79.0 kB view hashes)

Uploaded CPython 3.7m manylinux: glibc 2.5+ i686

numpy_api_bench-0.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl (106.5 kB view hashes)

Uploaded CPython 3.7m manylinux: glibc 2.12+ i686 manylinux: glibc 2.5+ i686

numpy_api_bench-0.1.0-cp37-cp37m-macosx_10_9_x86_64.whl (36.1 kB view hashes)

Uploaded CPython 3.7m macOS 10.9+ x86-64

numpy_api_bench-0.1.0-cp36-cp36m-win_amd64.whl (49.0 kB view hashes)

Uploaded CPython 3.6m Windows x86-64

numpy_api_bench-0.1.0-cp36-cp36m-win32.whl (46.2 kB view hashes)

Uploaded CPython 3.6m Windows x86

numpy_api_bench-0.1.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl (81.8 kB view hashes)

Uploaded CPython 3.6m manylinux: glibc 2.5+ x86-64

numpy_api_bench-0.1.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (106.1 kB view hashes)

Uploaded CPython 3.6m manylinux: glibc 2.12+ x86-64 manylinux: glibc 2.5+ x86-64

numpy_api_bench-0.1.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl (78.9 kB view hashes)

Uploaded CPython 3.6m manylinux: glibc 2.5+ i686

numpy_api_bench-0.1.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl (102.8 kB view hashes)

Uploaded CPython 3.6m manylinux: glibc 2.12+ i686 manylinux: glibc 2.5+ i686

numpy_api_bench-0.1.0-cp36-cp36m-macosx_10_9_x86_64.whl (36.1 kB view hashes)

Uploaded CPython 3.6m macOS 10.9+ x86-64

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page