Почему кортеж быстрее, чем список в Python?

Продолжение просто делает то, что предлагает его имя, оно немедленно продолжает следующее выполнение цикла без выполнения остальной части кода в цикле.

Итак, если у вас есть такой цикл:

int j=0;
int i = 0;
    LABEL1 : for(;i < 3; i++){
        if (true) {
            continue;
        }
        someOtherMethod();
    }

Часть someOtherMethod никогда не выполняется, потому что вы всегда будете нажимать на продолжение.

Причина, по которой ваш счетчик никогда не увеличивается, связан с метками, вы можете пометить цикл меткой (в вашем случае LABEL1 и LABEL2 и используйте эту метку, чтобы продолжить одну из внешних циклов вашего внутреннего цикла.

Таким образом, с вашим кодом цикл LABEL1 никогда не будет иметь шансов на увеличение его счетчик и, следовательно, i остаются 0, потому что ваш оператор продолжения немедленно продолжает следующую итерацию внешнего цикла.

63
задан maij 31 July 2019 в 06:36
поделиться

4 ответа

Сообщаемое соотношение «скорость построения» справедливо только для константных кортежей (тех, элементы которых выражены литералами). Внимательно наблюдайте (и повторите на своем компьютере - вам просто нужно вводить команды в оболочке / командном окне!) ...:

$ python3.1 -mtimeit -s'x,y,z=1,2,3' '[x,y,z]'
1000000 loops, best of 3: 0.379 usec per loop
$ python3.1 -mtimeit '[1,2,3]'
1000000 loops, best of 3: 0.413 usec per loop

$ python3.1 -mtimeit -s'x,y,z=1,2,3' '(x,y,z)'
10000000 loops, best of 3: 0.174 usec per loop
$ python3.1 -mtimeit '(1,2,3)'
10000000 loops, best of 3: 0.0602 usec per loop

$ python2.6 -mtimeit -s'x,y,z=1,2,3' '[x,y,z]'
1000000 loops, best of 3: 0.352 usec per loop
$ python2.6 -mtimeit '[1,2,3]'
1000000 loops, best of 3: 0.358 usec per loop

$ python2.6 -mtimeit -s'x,y,z=1,2,3' '(x,y,z)'
10000000 loops, best of 3: 0.157 usec per loop
$ python2.6 -mtimeit '(1,2,3)'
10000000 loops, best of 3: 0.0527 usec per loop

Я не проводил измерения на 3.0, потому что, конечно, у меня его нет вокруг - он полностью устарел, и нет абсолютно никаких причин для его хранения, поскольку 3.1 превосходит его во всех отношениях (Python 2.7, если вы можете перейти на него, измеряется как почти на 20% быстрее, чем 2.6 в каждой задаче - - и 2.6, как вы видите, быстрее, чем 3.1 - так что, если вы серьезно заботитесь о производительности, Python 2.7 - действительно единственный выпуск, на который вы должны идти!).

В любом случае, ключевым моментом здесь является то, что в каждом выпуске Python построение списка из константных литералов происходит примерно с той же скоростью или немного медленнее, чем построение его из значений, на которые ссылаются переменные; но кортежи ведут себя по-разному - построение кортежа из константных литералов обычно в три раза быстрее, чем построение его из значений, на которые ссылаются переменные! Вы можете задаться вопросом, как это может быть, не так ли? -)

Ответ: компилятор Python может легко идентифицировать кортеж, составленный из константных литералов, как один неизменяемый константный литерал: так что он, по сути, строится только один раз, когда компилятор преобразует исходный код в байт-коды и хранит их в «таблице констант» соответствующей функции или модуля.Когда эти байт-коды выполняются, им просто нужно восстановить предварительно созданный постоянный кортеж - привет, престо! -)

Эту простую оптимизацию нельзя применить к спискам, потому что список является изменяемым объектом, поэтому очень важно, чтобы, если такое же выражение, как [1, 2, 3] , выполняется дважды (в цикле - модуль timeit выполняет цикл от вашего имени ;-), новый новый объект списка - конструируется каждый раз заново - и это построение (например, построение кортежа, когда компилятор не может тривиально идентифицировать его как постоянную времени компиляции и неизменяемый объект) действительно занимает некоторое время.

При этом конструкция кортежа (когда обе конструкции фактически должны происходят) по-прежнему примерно в два раза быстрее, чем построение списка - и это несоответствие может быть объяснено чистой простотой кортежа, о чем неоднократно упоминалось в других ответах. Но эта простота не учитывает ускорение в шесть или более раз, как вы заметите, если только сравните построение списков и кортежей с простыми константными литералами в качестве их элементов! _)

88
ответ дан 24 November 2019 в 15:20
поделиться

Благодаря мощности модуля timeit вы часто можете самостоятельно решать вопросы, связанные с производительностью:

$ python2.6 -mtimeit -s 'a = tuple(range(10000))' 'for i in a: pass'
10000 loops, best of 3: 189 usec per loop
$ python2.6 -mtimeit -s 'a = list(range(10000))' 'for i in a: pass' 
10000 loops, best of 3: 191 usec per loop

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

$ python2.6 -mtimeit '(1, 2, 3, 4)'   
10000000 loops, best of 3: 0.0266 usec per loop
$ python2.6 -mtimeit '[1, 2, 3, 4]'
10000000 loops, best of 3: 0.163 usec per loop

Итак, если скорость итерации или индексации являются единственными факторами, фактически нет никакой разницы, но для построения кортежи выигрывают.

16
ответ дан 24 November 2019 в 15:20
поделиться

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

В CPython кортежи хранятся в одном блоке памяти, поэтому создание нового кортежа включает в худшем случае один вызов для выделения памяти. Списки размещаются в двух блоках: фиксированном со всей информацией об объектах Python и блоком переменного размера для данных. Это одна из причин, по которым создание кортежа происходит быстрее, но, вероятно, это также объясняет небольшую разницу в скорости индексации, поскольку нужно следовать на один указатель меньше.

В CPython также есть оптимизация для уменьшения выделения памяти: объекты невыделенного списка сохраняются в свободном списке, поэтому их можно использовать повторно, но выделение непустого списка по-прежнему требует выделения памяти для данных. Кортежи сохраняются в 20 свободных списках для кортежей разного размера, поэтому выделение небольшого кортежа часто вообще не требует каких-либо вызовов выделения памяти.

Подобные оптимизации полезны на практике, но они также могут сделать рискованным слишком сильно зависеть от результатов timeit и, конечно, будут совершенно другими, если вы перейдете к чему-то вроде IronPython, где распределение памяти работает совершенно иначе.

20
ответ дан 24 November 2019 в 15:20
поделиться

По сути, потому что неизменяемость кортежа означает, что интерпретатор может использовать для него более компактную и быструю структуру данных по сравнению со списком.

5
ответ дан 24 November 2019 в 15:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: