Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Модули на С. Инструменты и правила — разбираем на примере

Модули на С. Инструменты и правила — разбираем на примере

Алексей Бузанов (Программист, Mail.Ru Group)

Moscow Python Meetup
PRO

September 12, 2013
Tweet

More Decks by Moscow Python Meetup

Other Decks in Programming

Transcript

  1. Модули на C
    Инструменты и правила
    Алексей Бузанов

    View Slide

  2. Moscow Django MeetUp №14

    View Slide

  3. Moscow Django MeetUp №14

    View Slide

  4. Moscow Django MeetUp №14

    View Slide

  5. Moscow Django MeetUp №14
    def is_palindrome(num):
    x = str(num)
    return x == x[::-1]
    def palindrome_factors(digits):
    max_num = (10 ** digits) - 1
    min_num = max_num // 10
    best = (0, 0, 0)
    for i in range(max_num, min_num, -1):
    for j in range(i, min_num, -1):
    multi = i * j
    if multi > best[0] and is_palindrome(multi):
    best = (multi, i, j)
    return best

    View Slide

  6. Moscow Django MeetUp №14
    #include
    static int is_palindrome(int num, int digits) {
    int* check_array = malloc(digits * 2);
    int l = 0, i = 0;
    for (l = 0; num != 0; l++, num /= 10) {
    check_array[l] = num % 10;
    }
    for (i = 0; i < l/2; i++) {
    if (check_array[i] != check_array[l - i - 1]) {
    return 0;
    }
    }
    free(check_array);
    return 1;
    }

    View Slide

  7. Moscow Django MeetUp №14
    static PyObject*
    cpal_palindrome_factors(PyObject* self, PyObject* args) {
    int digits;
    if (!PyArg_ParseTuple(args, "i", &digits)) { return NULL; }
    int max_num=0, min_num=0, i, j, multi, best[3] = {0, 0, 0};
    max_num = pow(10, digits) - 1;
    min_num = max_num/10;
    for (i = max_num; i > min_num; i--) {
    for (j = i; j > min_num; j--) {
    multi = i * j;
    if (multi > best[0] && is_palindrome(multi, digits)) {
    best[0] = multi; best[1] = i; best[2] = j;
    }
    }
    }
    return Py_BuildValue("iii", best[0], best[1], best[2]);
    }

    View Slide

  8. Moscow Django MeetUp №14
    static PyMethodDef cpal_methods[] = {
    {"palindrome_factors", (PyCFunction)cpal_palindrome_factors,
    METH_VARARGS, "calc max palindrome factors"},
    {NULL, NULL, 0, NULL}
    };
    void initcpal(void) {
    Py_InitModule("cpal", cpal_methods);
    }

    View Slide

  9. Moscow Django MeetUp №14
    $ cat setup.py
    from distutils.core import setup, Extension
    setup(name='cpal',
    version='1.0',
    author = 'Alexey Buzanov',
    author_email = '[email protected]',
    ext_modules=[Extension('cpal', ['cpal.c'])])
    $ python setup.py build
    ...
    $ python setup.py install
    ...
    $ python setup.py sdist
    ...

    View Slide

  10. Moscow Django MeetUp №14
    $ time python run.py -d3
    Answer: 906609 = (993 x 913)
    real 0m0.092s
    $ time python run.py -d3 -c
    Answer: 906609 = (993 x 913)
    real 0m0.032s

    View Slide

  11. Moscow Django MeetUp №14
    $ python run.py -d4 -c
    *** Error in `python': free(): invalid next size (fast):
    0x0000000000f20510 ***
    ======= Backtrace: =========
    /lib/x86_64-linux-gnu/libc.so.6(+0x80a46)[0x7f15a36a0a46]
    …/site-packages/cpal.so(+0x9d1)[0x7f15a1fe29d1]
    ...

    View Slide

  12. Moscow Django MeetUp №14

    View Slide

  13. Moscow Django MeetUp №14
    $ ./configure --without-pymalloc --prefix=/opt/python27_gdb
    $ make && make install
    $ virtualenv -p /opt/python27_gdb/bin/python gdb_env
    $ source gdb_env/bin/activate
    $ CFLAGS="-O0" python setup.py build -g -f
    $ python setup.py install

    View Slide

  14. Moscow Django MeetUp №14
    $ gdb python
    (gdb) run run.py -d4 –c
    *** Error in `python': free(): invalid next size (fast):
    0x000000000090dba0 ***
    (gdb) bt
    #4 _int_free … at malloc.c:3758
    #5 0x00007ffff5aab9a8 in is_palindrome (num=0, digits=4)
    at cpal.c:16
    #6 0x00007ffff5aabab7 in cpal_palindrome_factors
    (self=0x0, args=0x941b50) at cpal.c:39
    (gdb) b cpal.c:16
    (gdb) run
    Breakpoint 1, is_palindrome (num=0, digits=4) at cpal.c:16
    16 free(check_array);

    View Slide

  15. Moscow Django MeetUp №14
    (gdb) p digits
    $1 = 4
    (gdb) p check_array[0]
    $2 = 9
    (gdb) p check_array[1]
    $3 = 9
    (gdb) p &check_array[0]
    $4 = (int *) 0x90dba0
    (gdb) p &check_array[1]
    $5 = (int *) 0x90dba4
    (gdb) l 5
    4 static int is_palindrome(int num, int digits) {
    5 int* check_array = malloc(digits * 2);
    6 int l = 0, i = 0;

    View Slide

  16. Moscow Django MeetUp №14
    $ cat Misc/gdbinit >> ~/.gdbinit
    (gdb) b cpal.c:23
    (gdb) run
    23 if (!PyArg_ParseTuple(args, "i", &digits)) {
    (gdb) pyo args
    object : (4,)
    type : tuple
    refcount: 1
    address : 0x941b50
    $7 = void

    View Slide

  17. Moscow Django MeetUp №14
    static int is_palindrome(int num, int digits) {
    int* check_array = malloc(digits * 2 * sizeof(int));
    int l = 0, i = 0;
    for (l = 0; num != 0; l++, num /= 10) {
    check_array[l] = num % 10;
    }
    for (i = 0; i < l/2; i++) {
    if (check_array[i] != check_array[l - i - 1]) {
    return 0;
    }
    }
    free(check_array);
    return 1;
    }

    View Slide

  18. Moscow Django MeetUp №14
    $ CFLAGS="-O0" python setup.py build -g -f &&
    python setup.py install
    $ time python run.py -d4 –c
    Answer: 99000099 = (9999 x 9901)
    real 0m0.105s
    $ time python run.py -d4
    Answer: 99000099 = (9999 x 9901)
    real 0m3.958s

    View Slide

  19. Moscow Django MeetUp №14

    View Slide

  20. Moscow Django MeetUp №14
    $ time python run.py -d5 -c
    Answer: 2147447412 = (99807 x 21516)
    real 0m8.641s
    $ time python run.py -d5
    Answer: 9966006699 = (99979 x 99681)
    real 6m41.028s

    View Slide

  21. Moscow Django MeetUp №14
    typedef unsigned PY_LONG_LONG pal_t;
    static int is_palindrome(pal_t num, int digits);
    static PyObject* cpal_palindrome_factors(PyObject* self,
    PyObject* args) {

    pal_t max_num = 0, min_num = 0, i, j, multi;
    pal_t best[3] = {0, 0, 0};

    return Py_BuildValue("KKK", best[0], best[1], best[2]);
    }

    View Slide

  22. Moscow Django MeetUp №14
    $ time python run.py -d5 -c
    Answer: 9966006699 = (99979 x 99681)
    real 0m9.003s

    View Slide

  23. Moscow Django MeetUp №14

    View Slide

  24. Moscow Django MeetUp №14
    $ valgrind --tool=memcheck --leak-check=full
    --show-possibly-lost=no python run.py -d4 -c
    Answer: 99000099 = (9999 x 9901)
    ...
    79,968 bytes in 2,499 blocks are definitely lost in loss
    record 1,635 of 1,636
    by 0x7B618CA: is_palindrome (cpal.c:8)
    by 0x7B61B55: cpal_palindrome_factors (cpal.c:46)
    by 0x4A1F7D: PyEval_EvalFrameEx (ceval.c:4021)
    ...
    LEAK SUMMARY:
    definitely lost: 79,968 bytes in 2,499 blocks
    possibly lost: 417,643 bytes in 2,380 blocks
    still reachable: 881,455 bytes in 6,097 blocks

    View Slide

  25. Moscow Django MeetUp №14
    static int is_palindrome(pal_t num, int digits) {
    int* check_array = malloc(digits * 2 * sizeof(int));
    int l = 0, i = 0;
    for (l = 0; num != 0; l++, num /= 10) {
    check_array[l] = num % 10;
    }
    for (i = 0; i < l/2; i++) {
    if (check_array[i] != check_array[l - i - 1]) {
    return 0;
    }
    }
    free(check_array);
    return 1;
    }

    View Slide

  26. Moscow Django MeetUp №14
    static int is_palindrome(pal_t num, int digits) {
    int* check_array = malloc(digits * 2 * sizeof(int));
    int l = 0, i = 0;
    for (l = 0; num != 0; l++, num /= 10) {
    check_array[l] = num % 10;
    }
    for (i = 0; i < l/2; i++) {
    if (check_array[i] != check_array[l - i - 1]) {
    free(check_array);
    return 0;
    }
    }
    free(check_array);
    return 1;
    }

    View Slide

  27. Moscow Django MeetUp №14
    $ valgrind --tool=memcheck --leak-check=full
    --show-possibly-lost=no python run.py -d4 -c
    Answer: 99000099 = (9999 x 9901)
    ...
    LEAK SUMMARY:
    definitely lost: 0 bytes in 0 blocks
    possibly lost: 417,643 bytes in 2,380 blocks
    still reachable: 881,455 bytes in 6,097 blocks
    suppressed: 0 bytes in 0 blocks

    View Slide

  28. Moscow Django MeetUp №14

    View Slide

  29. Moscow Django MeetUp №14
    #include
    #include "pal.c"
    static PyObject*
    cpal_palindrome_factors(PyObject* self, PyObject* args) {
    int digits;
    if (!PyArg_ParseTuple(args, "i", &digits)) {
    return NULL;
    }
    pal_t best[3] = {0, 0, 0};
    palindrome_factors(best, digits);
    return Py_BuildValue("KKK", best[0], best[1], best[2]);
    }

    View Slide

  30. Moscow Django MeetUp №14
    $ cat main.c
    #include /* malloc, free */
    #include /* printf */
    #include "pal.c"
    int main() {
    pal_t best[3] = {0, 0, 0};
    palindrome_factors(best, 4);
    printf("Answer: %llu (%llu x %llu)\n",
    best[0], best[1], best[2]);
    return 0;
    }
    $ gcc main.c -g -O0 -lm -o pal.bin
    $ ./pal.bin
    Answer: 99000099 (9999 x 9901)

    View Slide

  31. Moscow Django MeetUp №14
    $ valgrind --tool=massif --time-unit=B --threshold=0.001
    --detailed-freq=1 --stacks=yes ./pal.bin
    $ ms_print massif.out.25093
    $ massif-visualizer massif.out.25093

    View Slide

  32. Moscow Django MeetUp №14

    View Slide

  33. Moscow Django MeetUp №14
    static int is_palindrome(pal_t num, int* check_array)

    static void palindrome_factors(pal_t* best, int digits) {
    int* check_array = (int*)malloc(digits*2*sizeof(int));
    ...
    if (multi > best[0] && is_palindrome(multi, check_array))
    ...
    free(check_array);
    }

    View Slide

  34. Moscow Django MeetUp №14

    View Slide

  35. Moscow Django MeetUp №14
    $ time ./pal.bin
    Answer: 9966006699
    real 0m8.448s
    $ time ./pal.bin
    Answer: 9966006699
    real 0m8.406s

    View Slide

  36. Moscow Django MeetUp №14

    View Slide

  37. Moscow Django MeetUp №14

    View Slide

  38. Moscow Django MeetUp №14
    def palindrome_factors(digits):
    max_num = sum(9 * (10 ** i) for i in range(digits))
    min_num = max_num // 10
    best = (0, 0, 0)
    for i in xrange(max_num, min_num, -1):
    if i**2 <= best[0]:
    break
    for j in xrange(i, min_num, -1):
    multi = i * j
    if multi <= best[0]:
    break
    if is_palindrome(multi):
    best = (multi, i, j)
    return best

    View Slide

  39. Moscow Django MeetUp №14
    $ time python run.py -d5 -c
    Answer: 9966006699 = (99979 x 99681)
    real 0m8.437s
    $ time python run.py -d5
    Answer: 9966006699 = (99979 x 99681)
    real 0m3.731s

    View Slide

  40. Moscow Django MeetUp №14

    View Slide

  41. Moscow Django MeetUp №14

    View Slide

  42. Moscow Django MeetUp №14

    View Slide

  43. Moscow Django MeetUp №14

    View Slide