Почему int (len (str (k))) быстрее, чем цикл (while) - PullRequest
0 голосов
/ 26 ноября 2018

Мне интересно, почему эта функция:

def digit(k):
    return len(str(k))

быстрее этой?:

def digit(k):
    i = 0
    while k != 0:
        k = k // 10
        i += 1
    return i

А почему наоборот, например в C?

1 Ответ

0 голосов
/ 26 ноября 2018

Давайте посмотрим, что произойдет, если мы возьмем ваш код Python и переведем его как можно более буквально на C. Мы можем сделать это очень легко с помощью Cython :

# save this in a file named "testmod.pyx" and compile it with Cython and a
# C compiler - details vary depending on OS and Python installation

from libc.stdio cimport snprintf
from libc.string cimport strlen

def c_digit_loop(k_):
    cdef unsigned int k = k_
    cdef int i = 0
    while k != 0:
        k = k // 10
        i += 1
    return i

def c_digit_str(k_):
    cdef unsigned int k = k_
    cdef char strbuf[32] # more than enough for any 'unsigned int'
    snprintf(strbuf, sizeof(strbuf), "%u", k);
    return strlen(strbuf);

Машинакод, который вы получаете из этого, не так оптимален, как мог бы быть, но он достаточно близок для быстрого тестирования.Это позволяет нам сравнивать производительность напрямую, используя timeit, например:

# save this in a file named 'test.py' and run it using the
# same CPython you compiled testmod.pyx against

import timeit
from testmod import c_digit_loop, c_digit_str

def py_digit_loop(k):
    i = 0
    while k != 0:
        k = k // 10
        i += 1
    return i

def py_digit_str(k):
    return len(str(k))

def test1(name):
    print(name, timeit.timeit(name+"(1234567)", "from __main__ import "+name,
                              number=10000))

test1("py_digit_loop")
test1("py_digit_str")
test1("c_digit_str")
test1("c_digit_loop")

Когда я запускаю эту программу, это вывод, который я получаю на компьютере, где я печатаюэтот.Я вручную выстроил числа, чтобы их было проще сравнивать на глаз.

py_digit_loop 0.004024484000183293
py_digit_str  0.0020454510013223626
c_digit_str   0.0009924650003085844
c_digit_loop  0.00025072999960684683

Так что это подтверждает ваше первоначальное утверждение: цикл медленнее, чем преобразование в строку в Python, но в C этонаоборотНо обратите внимание, что преобразование в строку в C все еще быстрее, чем преобразование в строку в Python.

Чтобы точно знать, почему это происходит, 1017 * нам нужно глубже вникнуть в кишкиИнтерпретатор Python, чем мне хотелось бы этим утром, но я знаю достаточно о его смелости, чтобы рассказать вам в общих чертах.Интерпретатор CPython не очень эффективен.Даже операции с маленькими целыми числами включают подсчет ссылок и создание пустых объектов в куче.Ваш цикл, который выполняет базовую арифметику в Python, требует одного или двух чистых объектов на одну итерацию (в зависимости от того, "0, 1, 2, ..." интернированы ").Выполнение вычисления путем преобразования в строку и определения ее длины включает создание только одного временного объекта - строки для всего вычисления.Бухгалтерия, связанная с этими пустыми объектами, снижает стоимость фактических вычислений, для обеих реализаций Python .

Реализация на основе строки C выполняет почти те же шаги, что и строка Pythonоснованная на реализации реализация выполняет, но ее царапающий объект представляет собой массив char в стеке, а не полноценный строковый объект Python, и это само по себе, по-видимому, хорошо для ускорения на 40-50%.

Реализация на основе цикла C компилирует до восьми машинных инструкций для фактического цикла.Нет доступа к памяти.Даже не инструкция по аппаратному делению (магия снижения прочности ).А затем еще сотни инструкций, касающихся объектной модели Python.Большинство из этих 0,00025 секунд по-прежнему накладные расходы .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...