Давайте посмотрим, что произойдет, если мы возьмем ваш код 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 секунд по-прежнему накладные расходы .