Ответ 1
Нет, вы не застряли в быстрой сортировке. Вы могли бы использовать, например,
integer_sort
от
Boost.Sort
или u4_sort
из usort. При сортировке этого массива:
array(randint(0, high=1<<32, size=10**8), uint32)
Получаю следующие результаты:
NumPy quicksort: 8.636 s 1.0 (baseline) Boost.Sort integer_sort: 4.327 s 2.0x speedup usort u4_sort: 2.065 s 4.2x speedup
Я бы не стал делать выводы на основе этого единственного эксперимента и использовать
usort
слепо. Я бы проверил свои фактические данные и измерил, что произойдет.
Ваш пробег будет изменяться в зависимости от ваших данных и вашего устройства.
integer_sort
в Boost.Sort имеет богатый набор настроек для настройки, см.
.
Ниже я описываю два способа вызова собственной C или С++ функции из Python. Несмотря на длинное описание, это довольно легко сделать.
Boost.Sort
Поместите эти строки в файл spreadsort.cpp:
#include <cinttypes>
#include "boost/sort/spreadsort/spreadsort.hpp"
using namespace boost::sort::spreadsort;
extern "C" {
void spreadsort(std::uint32_t* begin, std::size_t len) {
integer_sort(begin, begin + len);
}
}
Он в основном создает шаблоны integer_sort
для 32-битного
целые числа без знака; часть extern "C"
обеспечивает связь C путем отключения
имя mangling.
Предполагая, что вы используете gcc и что необходимые файлы включения boost
находятся в каталоге /tmp/boost_1_60_0
, вы можете скомпилировать его:
g++ -O3 -std=c++11 -march=native -DNDEBUG -shared -fPIC -I/tmp/boost_1_60_0 spreadsort.cpp -o spreadsort.so
Ключевыми флагами являются -fPIC
для генерации
код, не зависящий от позиции
и -shared
для генерации
общий объект
.so файл. (Читайте документы gcc для получения дополнительной информации.)
Затем вы завершаете функцию spreadsort()
С++
в Python, используя ctypes
:
from ctypes import cdll, c_size_t, c_uint32
from numpy import uint32
from numpy.ctypeslib import ndpointer
__all__ = ['integer_sort']
# In spreadsort.cpp: void spreadsort(std::uint32_t* begin, std::size_t len)
lib = cdll.LoadLibrary('./spreadsort.so')
sort = lib.spreadsort
sort.restype = None
sort.argtypes = [ndpointer(c_uint32, flags='C_CONTIGUOUS'), c_size_t]
def integer_sort(arr):
assert arr.dtype == uint32, 'Expected uint32, got {}'.format(arr.dtype)
sort(arr, arr.size)
В качестве альтернативы вы можете использовать cffi:
from cffi import FFI
from numpy import uint32
__all__ = ['integer_sort']
ffi = FFI()
ffi.cdef('void spreadsort(uint32_t* begin, size_t len);')
C = ffi.dlopen('./spreadsort.so')
def integer_sort(arr):
assert arr.dtype == uint32, 'Expected uint32, got {}'.format(arr.dtype)
begin = ffi.cast('uint32_t*', arr.ctypes.data)
C.spreadsort(begin, arr.size)
При вызовах cdll.LoadLibrary()
и ffi.dlopen()
я предположил, что
путь к файлу spreadsort.so
./spreadsort.so
. С другой стороны,
вы можете написать
lib = cdll.LoadLibrary('spreadsort.so')
или
C = ffi.dlopen('spreadsort.so')
если вы добавите путь к spreadsort.so
в среду LD_LIBRARY_PATH
переменная. См. Также Общие библиотеки.
Использование. В обоих случаях вы просто вызываете вышеуказанную функцию-оболочку Python integer_sort()
с вашим массивом numpy из 32-битных целых чисел без знака.
usort
Что касается u4_sort
, вы можете скомпилировать его следующим образом:
cc -DBUILDING_u4_sort -I/usr/include -I./ -I../ -I../../ -I../../../ -I../../../../ -std=c99 -fgnu89-inline -O3 -g -fPIC -shared -march=native u4_sort.c -o u4_sort.so
Выполните эту команду в каталоге, где находится файл u4_sort.c
.
(Наверное, есть менее хакерский способ, но я не понял этого.
просто просмотрел файл deps.mk в каталоге usort, чтобы узнать
необходимые флаги компилятора и включить пути.)
Затем вы можете обернуть функцию C следующим образом:
from cffi import FFI
from numpy import uint32
__all__ = ['integer_sort']
ffi = FFI()
ffi.cdef('void u4_sort(unsigned* a, const long sz);')
C = ffi.dlopen('u4_sort.so')
def integer_sort(arr):
assert arr.dtype == uint32, 'Expected uint32, got {}'.format(arr.dtype)
begin = ffi.cast('unsigned*', arr.ctypes.data)
C.u4_sort(begin, arr.size)
В приведенном выше коде я предположил, что путь к u4_sort.so
был
добавляется к переменной среды LD_LIBRARY_PATH
.
Использование. Как и раньше, с помощью Boost.Sort вы просто вызываете вышеуказанную функцию-оболочку Python integer_sort()
с помощью массива numpy из 32-битных целых чисел без знака.