Уведомления

Группа в Telegram: @pythonsu

#1 Июль 19, 2017 16:47:17

Master_Sergius
Зарегистрирован: 2013-09-12
Сообщения: 271
Репутация: +  7  -
Профиль   Отправить e-mail  

Хороший работающий гайд по написанию модуля расширения на С

Здравствуйте. Для проверки скорости и выбора средства для ускорения программы на Python Я пробую реализовать простой пример с двумя вложенными циклами.
Итак, два вложенных цикла от 1 до 10000 чисто на питоне на моей машине выполняются примерно за 4.5-5 секунд. С использованием Cython - 2.7-3.1. Чисто на С - 0.25-0.35.
Вот решил ещё попробовать написать сам с нуля модуль, вот смотрю на эти вещи:
https://docs.python.org/3/extending/extending.html
https://stackoverflow.com/questions/43621948/c-python-module-import-error-undefined-symbol-py-initmodule3-py-initmodu

А вот мой setup.py:

 from distutils.core import setup, Extension
setup(name='loops_lib', version='1.0',  \
      ext_modules=[Extension('loops_lib', ['loops_lib_for_python.c'])])

В общем, не получается написать/заставить работать. Либо билдится, но не могу использовать, либо не билдится вообще. Вот такие ошибки в разных вариантах:

 ImportError: loops_lib.cpython-35m-x86_64-linux-gnu.so: undefined symbol: Py_InitModule3

либо так
 ImportError: dynamic module does not define module export function (PyInit_loops_lib)

Даже полностью готовый рабочий пример чего-нибудь мне найти не удалось. Одни пишут, что то устарело, другие - то. Как говорила одна особь - сложна, сложна!
Подскажите хороший современный гайд, пожалуйста, желательно для Python 3

===

Обновление #1: выложу таки свои попытки что-то сделать:
 #include<Python.h>
static PyObject *nested_loops_2(PyObject *self, PyObject *args) {
    long int count1, count2;
    unsigned long long int counter;
    if (!PyArg_ParseTuple(args, "ll", &count1, &count2)) {
        return NULL;
    }
    counter = 0;
    for (int i=0; i<count1; i++)
        for (int j=0; j<count1; j++)
            counter += 1;
    return Py_BuildValue("llu", counter);
}
static char nested_loops_2_docs[] = "nested_loops_2( ): Run 2 nested loops and count iterations\n";
//static PyMethodDef nested_loops_2_funcs[] = {
//    {"nested_loops_2", (PyCFunction)nested_loops_2,
//    METH_VARARGS, nested_loops_2_docs},
//    {NULL, NULL, 0, NULL}
//};
static PyMethodDef module_methods[] = {
    {"nested_loops_2", (PyCFunction) nested_loops_2,
    METH_VARARGS, nested_loops_2_docs},
    {NULL, NULL, 0, NULL}
 };
static struct PyModuleDef struct_nested_loops_2 =
{
    PyModuleDef_HEAD_INIT,
    "loops_lib",
    NULL,
    -1,
    module_methods
};
//void init_nested_loops_2(void) {
//    Py_InitModule3("nested_loops_2", module_methods);
//}
//PyMODINIT_FUNC PyInit_nested_loops_2(void)
//{
//    return PyModule_Create(&struct_nested_loops_2);
//}



———————————————————————————
Мой блог о семействе *nix: http://nixtravelling.blogspot.com/

Отредактировано Master_Sergius (Июль 19, 2017 18:09:39)

Офлайн

#2 Июль 20, 2017 08:37:44

Shaman
Зарегистрирован: 2013-03-15
Сообщения: 1369
Репутация: +  88  -
Профиль   Отправить e-mail  

Хороший работающий гайд по написанию модуля расширения на С

Если тут поменять

PyMODINIT_FUNC PyInit_loops_lib(void)
{
return PyModule_Create(&struct_nested_loops_2);
}
и тут
static PyObject *nested_loops_2(PyObject *self, PyObject *args) {
long int count1, count2;
unsigned long long int counter;
if (!PyArg_ParseTuple(args, "ll", &count1, &count2)) {
return NULL;
}
counter = 0;
for (int i=0; i<count1; i++)
for (int j=0; j<count1; j++)
counter += 1;
return Py_BuildValue("K", counter);
}
тогда оно запустится (на третьем)
Ещё хотелось бы глянуть как Вы Cython тестировали, а то результаты очень неожиданные.

Офлайн

#3 Июль 20, 2017 18:05:23

Master_Sergius
Зарегистрирован: 2013-09-12
Сообщения: 271
Репутация: +  7  -
Профиль   Отправить e-mail  

Хороший работающий гайд по написанию модуля расширения на С

Да, так работает. А можно узнать где Вы это взяли? Должна же быть где-то нормальная и свежая документация. Что странно, так это то, что оно быстрее даже за чисто С-шный пример (или это Я такой С программер).
Насчёт Cython, loops_cython.pyx:

 def nested_loops_2(count1, count2):
    counter = 0
    for index1 in range(count1):
        for index2 in range(count2):
            counter += 1
    return counter
для него setup.py:
 from distutils.core import setup
from Cython.Build import cythonize
 
setup(
  name = 'nested loops',
    ext_modules = cythonize("loops_cython.pyx"),
    )

И тест:

 #!/usr/bin/env python3
 
import sys
 
from loops_cython import nested_loops_2
 
print(nested_loops_2(int(sys.argv[1]), int(sys.argv[2])))

Ну и можно взглянyть на loops.c:

 #include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
    char *p;
    long int count1 = strtol(argv[1], &p, 10);
    long int count2 = strtol(argv[2], &p, 10);
    unsigned long long int counter = 0;
    for (int i=0; i<count1; i++)
        for(int j=0; j<count2; j++)
            counter++;
    printf("%llu\n", counter);
    return 0;
}



———————————————————————————
Мой блог о семействе *nix: http://nixtravelling.blogspot.com/

Офлайн

#4 Июль 20, 2017 19:38:58

Shaman
Зарегистрирован: 2013-03-15
Сообщения: 1369
Репутация: +  88  -
Профиль   Отправить e-mail  

Хороший работающий гайд по написанию модуля расширения на С

Master_Sergius
А можно узнать где Вы это взяли?
Из питоновской документации в основном. Полноценных туториалов не попадалось: чуть там урвал, чуть в другом месте. При этом приходится иметь в виду отличия в апи у разных версий питона.
С цитоном картина прояснилась. Вы не расставили типы, из-за чего транслятор вынужден тянуть волынку с питоновскими объектами. Попробуйте теперь:
cpdef unsigned long long nested_loops_2(long count1, long count2):
cdef long index1, index2
cdef unsigned long long counter = 0
for index1 in range(count1):
for index2 in range(count2):
counter += 1
return counter
Это оттранслируется в
static unsigned PY_LONG_LONG __pyx_f_3tst_nested_loops_2(long __pyx_v_count1, long __pyx_v_count2, CYTHON_UNUSED int __pyx_skip_dispatch) {
CYTHON_UNUSED long __pyx_v_index1;
CYTHON_UNUSED long __pyx_v_index2;
unsigned PY_LONG_LONG __pyx_v_counter;
unsigned PY_LONG_LONG __pyx_r;
__Pyx_RefNannyDeclarations
long __pyx_t_1;
long __pyx_t_2;
long __pyx_t_3;
long __pyx_t_4;
__Pyx_RefNannySetupContext("nested_loops_2", 0);
__pyx_v_counter = 0;
__pyx_t_1 = __pyx_v_count1;
for (__pyx_t_2 = 0; __pyx_t_2 < __pyx_t_1; __pyx_t_2+=1) {
__pyx_v_index1 = __pyx_t_2;
__pyx_t_3 = __pyx_v_count2;
for (__pyx_t_4 = 0; __pyx_t_4 < __pyx_t_3; __pyx_t_4+=1) {
__pyx_v_index2 = __pyx_t_4;
__pyx_v_counter = (__pyx_v_counter + 1);
}
}
__pyx_r = __pyx_v_counter;
goto __pyx_L0;
__pyx_L0:;
__Pyx_RefNannyFinishContext();
return __pyx_r;
}
+ обёртка поверх для взаимодействия с объектами Питона.
Кстати, по цитону в Сети есть много статей на русском.
Есть ещё вариант использовать с++ с boost python.

Офлайн

#5 Июль 21, 2017 14:20:55

Master_Sergius
Зарегистрирован: 2013-09-12
Сообщения: 271
Репутация: +  7  -
Профиль   Отправить e-mail  

Хороший работающий гайд по написанию модуля расширения на С

Да уж, скорость Cython теперь почти такая же, как и у расширения, с которым вот были проблемы. Единственное, что непонятно, как они отрабатывают быстрее за чисто С. Если С за 0.2 делает, то Cython и расширения за 0.1, что-то неверятное )



———————————————————————————
Мой блог о семействе *nix: http://nixtravelling.blogspot.com/

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version