Skip to content

Instantly share code, notes, and snippets.

@dtoma
Last active August 30, 2019 10:01
Show Gist options
  • Save dtoma/1ddc59875f4a9fc5b23e81b867dd7574 to your computer and use it in GitHub Desktop.
Save dtoma/1ddc59875f4a9fc5b23e81b867dd7574 to your computer and use it in GitHub Desktop.
cython libdiv

Notes

  • Easier to wrap the C API?
  • Need SIMD to scale with pandas/numpy
  • Can use OpenMP

Proof of concept

# setup.py
from distutils.core import setup
from distutils.extension import Extension

from Cython.Build import cythonize

extensions = [
    Extension(
        'divide',
        sources=['divide.pyx', 'main.cpp'],
        extra_compile_args=['-Ofast', '-fopenmp', '-march=native'],
        extra_link_args=['-fopenmp'],
        language="c++",
    )
]
setup(
    name='divide',
    ext_modules=cythonize(extensions),
)
# divide.pyx
from libcpp.vector cimport vector

cdef extern from "binding.h":
    cdef vector[long int] divide(vector[long int], int)

def do_div(x, y):
    return divide(<vector[long int]>x, <int>y)
// main.cpp
#include <vector>

#include "libdivide.h"

#include "binding.h"

std::vector<int64_t> divide(std::vector<int64_t> vect, int64_t divisor) {
  libdivide::divider<int64_t> fast_d(divisor);

  int len = vect.size();

  // Fast, computes division using libdivide
#pragma omp parallel
  {
  #pragma omp for
    for (int i = 0; i < len; i++) {
      vect[i] /= fast_d;
    }
  }

  return vect;
}
// binding.h
#pragma once

template <typename T>
class Vector;

std::vector<int64_t> divide(std::vector<int64_t> vect, int64_t divisor);

Benchmark

import pandas as pd

from divide import do_div

l = pd.Series(range(0, 100000, 2))
expected = [x / 2 for x in l]

def test_do_div(benchmark):
    r = benchmark(do_div, l.values, 2)
    assert r == expected

def test_div(benchmark):
    r = benchmark(lambda x, y: [z // y for z in x], l.values, 2)
    assert r == expected

def test_pd(benchmark):
    r = benchmark(lambda s, d: s // d, l, 2)
    assert pd.np.array_equal(r.values, expected)
---------------------------------------------------------------------------------------------- benchmark: 3 tests ----------------------------------------------------------------------------------------------
Name (time in us)             Min                    Max                   Mean                StdDev                 Median                   IQR            Outliers         OPS            Rounds  Iterations
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
test_pd                  622.5750 (1.0)       2,694.6820 (1.0)         951.7428 (1.0)        349.7697 (1.0)         803.2140 (1.0)        529.8907 (1.0)          91;7  1,050.7041 (1.0)         409           1
test_do_div            8,371.0710 (13.45)    25,566.2690 (9.49)     10,195.2598 (10.71)    2,848.5078 (8.14)      9,255.6070 (11.52)    1,040.2705 (1.96)        11;14     98.0848 (0.09)        120           1
test_div              16,505.9700 (26.51)    31,855.5470 (11.82)    19,863.2356 (20.87)    3,651.7666 (10.44)    18,423.3260 (22.94)    3,387.7065 (6.39)          6;3     50.3443 (0.05)         49           1
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment