Предположим, что я ищу индекс some_array
, где some_array
равно target
. Я знаю, что в python есть понимание списков и np.where()
, оба из которых хорошо подходят для моих целей. Но также предположим, что я хочу сделать это с операторами if-elif-else
или с циклом for
. Реализации, если длина массива равна 3, будут выглядеть так:
if some_array[0]==target:
return 0
elif some_array[1]==target:
return 1
else:
return 2
for i in range(3):
if some_array[i]==target:
return i
Итак, когда лучше использовать цикл for вместо оператора if-elif-else? Меня больше всего интересуют его применения в python и C, т. е. switch-cases
.
Мои подвопросы будут такими:
numba
или cython
) с цикла for
на switch-cases
или наоборот, если им кажется, что другой подход быстрее?if-elif-else
для лучшей читабельности?Прошу прощения, если это задано раньше. Я попытался проверить предложенные вопросы, но они не помогли мне.
Заранее спасибо!
Switch-cases обычно заменяется таблицей переходов, которая не всегда быстрее, чем набор условных переходов. Некоторые компиляторы адаптируют сгенерированный код на основе этого. Тем не менее, выбор зависит от входного набора данных, который компилятор не знает, поэтому, как правило, он не может создать эффективный код. Все это связано со сложными темами: предсказание переходов и декодирование инструкций. Из-за этого в некоторых случаях циклы могут работать быстрее, чем развернутый код. Вот почему удобочитаемость и профилирование имеют большое значение.
В большинстве случаев лучше позволить компиляторам сделать всю работу за вас, так как выбор также зависит от архитектуры. Хорошие компиляторы могут генерировать быстрый код SIMD, когда число итераций велико (хотя инструкции SIMD на самом деле могут быть медленнее в отношении варианта использования и входных данных). Обычно они не делают этого с набором условных выражений.
@JérômeRichard Большое спасибо! Это было так же полезно, как и ответы.
Производительность if
не будет иметь значения. Цикл for
был бы лучше, чем множество if для удобства чтения.
В этом конкретном случае просто используйте встроенную функцию, которая обычно работает быстрее.
return some_array.index(target)
Если возможно, что target
нет в списке
try:
return some_array.index(target)
except ValueError:
return -1
Спасибо, у вас есть мой голос. Но я не просил встроенные методы. Как я уже сказал, я знаю несколько из них, и не все циклы запрашивают значение определенного значения массива.
Единственный способ узнать конкретную производительность — протестировать свой код на нескольких машинах. Большинство реализаций Python не делают много оптимизаций.
Я понимаю, что получить точное пороговое значение будет невозможно, если только кто-нибудь не поиграет с демоном Максвелла для получения предельно математического ответа. Но все еще может быть приблизительный ответ для порога.
Приблизительный ответ: «просто используйте цикл» ИМО. Мне нравится наблюдение в другом ответе, что компилятор может легко развернуть цикл в тех случаях, когда это выгодно, но он не может легко пойти в другом направлении. Это говорит о том, что с точки зрения программиста лучше всегда использовать цикл в коде, а не пытаться угадать компилятор (поскольку компилятор почти всегда будет лучше вас в оптимизации машинного кода).
Итак, когда лучше использовать цикл for вместо оператора if-elif-else?
Петля всегда понятнее, чем цепочка if
-else if
-else
для этой конкретной цели. Это достаточная причина, чтобы предпочесть цикл, за исключением, возможно, очень маловероятного случая, когда вы проследите узкое место производительности в таком цикле и обнаружите, что его можно облегчить, заменив на if
.
- Компиляторы (или, в случае Python, numba или cython) переключаются с цикла for на switch-case или наоборот, если это похоже на другой подход быстрее?
Развертывание цикла — это стандартная оптимизация, которую выполняют многие компиляторы C, когда считают, что это даст улучшение. Короткая петля может быть полностью развёрнута, что и является той трансформацией, о которой вы спрашиваете.
Я не знаю компиляторов, выполняющих обратное преобразование.
- Существует ли обычно ожидаемая хорошая практика кодирования, которая предлагает максимальную длину для операторов if-elif-else, чтобы обеспечить простоту? следуя коду?
Не как таковой.
Пишите понятный код и не повторяйтесь.
- Существует ли порог длины массива или количества итераций, при котором один из них работает лучше, чем другой?
Не в частности. Производительность зависит от множества факторов и нескольких четких правил.
В общем, сначала заставить работать, потом, если нужно, сделать быстро.
«Повторное использование кода» всегда лучше, чем «копировать/вставить/адаптировать»… за исключением, пожалуй, тривиального случая двух операций.