Это был вопрос интервью, заданный старшим менеджером.
Что быстрее?
while(1) {
// Some code
}
или
while(2) {
//Some code
}
Я сказал, что оба имеют одинаковую скорость выполнения, так как выражение внутри while
должно наконец вычислить true
или false
. В этом случае оба true
выполняются, и внутри while
условия нет никаких дополнительных условных инструкций . Таким образом, оба будут иметь одинаковую скорость выполнения, и я предпочитаю while (1).
Но интервьюер уверенно сказал: «Проверьте свои основы. while(1)
Это быстрее, чем while(2)
». (Он не проверял мою уверенность)
Это правда?
См. Также: "for (;;)" быстрее, чем "while (TRUE)"? Если нет, то почему люди используют это?
c
performance
while-loop
Николе
источник
источник
0x100000f90: jmp 0x100000f90
(адрес варьируется, очевидно) для обоих фрагментов. Интервьюер, вероятно, хеджировался в тесте регистра против простого помеченного прыжка. И вопрос, и их предположение неубедительны.Ответы:
Оба цикла бесконечны, но мы можем видеть, какой из них требует больше инструкций / ресурсов за одну итерацию.
Используя gcc, я скомпилировал две следующие программы для сборки на разных уровнях оптимизации:
Даже без оптимизации (
-O0
) сгенерированная сборка была идентична для обеих программ . Следовательно, нет разницы в скорости между двумя петлями.Для справки вот сгенерированная сборка (используется
gcc main.c -S -masm=intel
с флагом оптимизации):С
-O0
:С
-O1
:С
-O2
и-O3
(тот же вывод):Фактически, сборка, сгенерированная для цикла, идентична для каждого уровня оптимизации:
Важными моментами являются:
Я не могу читать сборку очень хорошо, но это, безусловно, безусловный цикл.
jmp
Инструкция безоговорочно сбрасывает программу обратно на.L2
этикетке, даже не сравнивая значение против истины, и, конечно , сразу же делает это снова , пока программа не будет каким - то образом закончилась. Это напрямую соответствует коду C / C ++:Редактировать:
Интересно, что даже без оптимизации все следующие циклы производили одинаковый вывод (безусловный
jmp
) в сборке:И даже к моему изумлению:
Все становится немного интереснее с пользовательскими функциями:
На
-O0
самом деле, эти два примера фактически вызываютx
и выполняют сравнение для каждой итерации.Первый пример (возвращается 1):
Второй пример (возвращение
sqrt(7)
):Однако, как
-O1
и выше, они оба производят ту же сборку, что и предыдущие примеры (безусловноеjmp
возвращение к предыдущей метке).TL; DR
В GCC различные циклы компилируются в одинаковую сборку. Компилятор оценивает значения констант и не выполняет никакого фактического сравнения.
Мораль этой истории такова:
источник
-O
уровне.jmp
вернутся в начало, либо полностью удалят цикл.break
операторов), но я все равно протестировал его, и нет, даже с кодом внутри цикла переход абсолютный и безусловный для обоихwhile(1)
иwhile(2)
. Не стесняйтесь проверять других самостоятельно, если вы действительно обеспокоены.Да,
while(1)
намного быстрее , чемwhile(2)
, для человека , чтобы читать! Если я вижуwhile(1)
в незнакомой кодовой базе, я сразу знаю, что задумал автор, и мои глазные яблоки могут перейти к следующей строке.Если я увижу
while(2)
, я, вероятно, остановлюсь в своих треках и попытаюсь выяснить, почему автор не написалwhile(1)
. У автора проскользнул палец на клавиатуре? Используют ли разработчики этой кодовой базыwhile(n)
в качестве скрытого механизма комментирования, чтобы циклы выглядели иначе? Это грубый обходной путь для ложного предупреждения в каком-то сломанном инструменте статического анализа? Или это подсказка, что я читаю сгенерированный код? Это ошибка, возникшая из-за опрометчивого поиска и замены всех, или неудачного слияния, или космического луча? Может быть, эта строка кода должна сделать что-то совершенно другое. Может быть, это должно было прочитатьwhile(w)
илиwhile(x2)
. Я бы лучше нашел автора в истории файла и отправил им электронное письмо "WTF" ... и теперь я нарушил свой ментальный контекст.while(2)
while(1)
заняло бы долю секунды!Я преувеличиваю, но только немного. Читаемость кода действительно важна. И это стоит упомянуть в интервью!
источник
svn-annotate
илиgit blame
(или что-то еще) будет использоваться здесь, и в общем случае загрузка истории обвинений в файлах занимает минуты. Тогда, наконец, решить, «а я понимаю, автор написал эту строку, когда только что закончил старшую школу», просто потерял 10 минут ...1
.while(2)
в коде (вплоть до рассмотрения вопроса: «Используют ли сопровождающие эту кодовую базу while (n) как неясный механизм комментирования, чтобы циклы выглядели по-другому?») ). Так что нет, вы не преувеличиваете!Существующие ответы, показывающие код, сгенерированный конкретным компилятором для конкретной цели с определенным набором параметров, не полностью отвечают на вопрос - если только вопрос не был задан в этом конкретном контексте («Что быстрее при использовании gcc 4.7.2 для x86_64» с параметрами по умолчанию? ", например).
Что касается определения языка, в абстрактной машине
while (1)
оценивается целочисленная константа1
иwhile (2)
вычисляется целочисленная константа2
; в обоих случаях результат сравнивается на равенство нулю. Языковой стандарт абсолютно ничего не говорит об относительной производительности двух конструкций.Я могу себе представить, что чрезвычайно наивный компилятор может генерировать различный машинный код для двух форм, по крайней мере, при компиляции без запроса оптимизации.
С другой стороны, компиляторы C обязательно должны оценивать некоторые константные выражения во время компиляции, когда они появляются в контекстах, которые требуют константного выражения. Например, это:
требует диагностики; Ленивый компилятор не имеет возможности отложить оценку
2+2
до времени выполнения. Поскольку компилятор должен иметь возможность оценивать константные выражения во время компиляции, у него нет веских оснований не использовать эту возможность, даже если она не требуется.Стандарт C ( N1570 6.8.5p4) гласит, что
Таким образом, соответствующие константные выражения
1 == 0
и2 == 0
, оба из которых оцениваютint
значение0
. (Эти сравнения неявны в семантикеwhile
цикла; они не существуют как фактические выражения C).Извращенно наивный компилятор может генерировать разный код для двух конструкций. Например, для первого он может генерировать безусловный бесконечный цикл (рассматриваемый
1
как особый случай), а для второго он может генерировать явное сравнение во время выполнения, эквивалентное2 != 0
. Но я никогда не сталкивался с компилятором C, который бы на самом деле вел себя таким образом, и я серьезно сомневаюсь, что такой компилятор существует.Большинство компиляторов (я хочу сказать, что все компиляторы производственного качества) имеют опции для запроса дополнительной оптимизации. При такой опции еще менее вероятно, что любой компилятор сгенерирует разный код для двух форм.
Если ваш компилятор генерирует разный код для двух конструкций, сначала проверьте, действительно ли разные последовательности кода имеют разную производительность. Если это так, попробуйте снова скомпилировать с опцией оптимизации (если есть). Если они все еще различаются, отправьте отчет об ошибке поставщику компилятора. Это не обязательно ошибка в смысле несоответствия стандарту C, но это почти наверняка проблема, которую следует исправить.
Итог:
while (1)
иwhile(2)
почти наверняка имеют одинаковую производительность. Они имеют одинаковую семантику, и у любого компилятора нет веской причины не генерировать идентичный код.И хотя для компилятора совершенно законно генерировать более быстрый код,
while(1)
чем дляwhile(2)
, для компилятора одинаково законно генерировать более быстрый код,while(1)
чем для другого вхожденияwhile(1)
в той же программе.(Есть еще один вопрос, неявный из того, который вы задали: как вы справляетесь с интервьюером, который настаивает на неправильной технической точке. Это, вероятно, будет хорошим вопросом для сайта Workplace ).
источник
while
должно иметь скалярный тип, и тело цикла повторяется до тех пор, пока выражение не сравняется с 0. Он не говорит, что существует неявное значениеexpr != 0
, которое оценивается ..., которое потребовало бы результата что - 0 или 1 - в свою очередь сравнивать с 0 до бесконечности. Нет, выражение сравнивается с 0, но это сравнение не дает значения. PS Я проголосовал.<expr> == 0
; это то, что «сравнивает равно 0» означает в C. Это сравнение является частью семантикиwhile
цикла. Ни в стандарте, ни в моем ответе нет никакого смысла в том, что результат нужно сравнивать0
снова. (Я должен был написать,==
а не!=
.) Но эта часть моего ответа была неясной, и я обновлю ее.while (2)
,while (pointer)
,while (9.67)
, самым наивным, неоптимизированном компилятором, вы не увидите никакого кода , сгенерированного , что урожайность0
или1
. «Я должен был написать ==, а не! =» - нет, это не имело бы никакого смысла.... == 0
выражения C. Моя точка зрения заключается в том, что как «сравнение равно 0», требуемое стандартным описаниемwhile
циклов, так и явноеx == 0
выражение логически подразумевают одну и ту же операцию. И я думаю, что мучительно наивный компилятор C может генерировать код, который генерируетint
значение0
или1
для любогоwhile
цикла - хотя я не верю, что какой-либо реальный компилятор настолько наивен.Подожди минуту. Интервьюер, он похож на этого парня?
Достаточно того, что сам интервьюер провалил это интервью, а что если другие программисты в этой компании «сдали» этот тест?
Нет. Оценивать высказывания
1 == 0
и2 == 0
следует одинаково быстро. Мы могли бы представить плохие реализации компилятора, где один может быть быстрее другого. Но нет веской причины, почему один должен быть быстрее другого.Даже если есть какое-то неясное обстоятельство, когда утверждение было бы правдой, программисты не должны оцениваться на основе знания неясных (и в этом случае жутких) мелочей. Не беспокойтесь об этом интервью, лучший шаг здесь - уйти.
Отказ от ответственности: это не оригинальный мультфильм Дилберта. Это просто мэшап .
источник
cmp
операнд работать меньше циклов, сравнивая 1 с 0, чем 2 с 0? сколько циклов требуется для выполнения cmp в целом? это переменная в соответствии с битовыми шаблонами? они более «сложные» битовые паттерны, которые замедляютсяcmp
? Я не знаю лично. Вы могли бы представить супердидиотическую реализацию, проверяющую побитно от ранга 0 до n (например, n = 31).cmp
операнд должен быть одинаково быстрым для 1 и 200. Возможно, мы могли бы представить идиотские реализации, где это не так. Но можем ли мы представить неидиотическую реализацию, гдеwhile(1)
быстрее, чемwhile(200)
? Точно так же, если в какой-то доисторический период единственная доступная реализация была такой идиотской, стоит ли нам суетиться об этом сегодня? Я так не думаю, это заостренный разговор с боссом, и настоящая жемчужина в этом!Ваше объяснение верно. Это, кажется, вопрос, который проверяет вашу уверенность в себе в дополнение к техническим знаниям.
Кстати, если вы ответили
интервьюер скажет
Таким образом, отвечая, как вы, вы сэкономили время, которое иначе потратили бы на обсуждение этого плохого вопроса.
Вот пример кода, сгенерированного компилятором в моей системе (MS Visual Studio 2012) с отключенными оптимизациями:
С включенными оптимизациями:
Таким образом, сгенерированный код точно такой же, по крайней мере, с оптимизирующим компилятором.
источник
while
давно предшествовала ему), и операндwhile
не имеет логического или _Bool или любого другого определенного типа. Операндомwhile
может быть любое выражение ... while прерывается на 0 и продолжается не на 0.while (00000001) {}
наwhile (00000000) {}
. Чем больше истинных битов, тем меньше вероятность того, что значение перевернется в false. К сожалению, 2 также имеет только один истинный бит. 3, однако, будет работать значительно дольше. Это также относится только к компилятору, который не всегда оптимизирует это (VC ++).Наиболее вероятное объяснение этого вопроса заключается в том, что интервьюер считает, что процессор проверяет отдельные биты чисел, один за другим, пока не достигнет ненулевого значения:
Если "ноль?" Алгоритм запускается с правой стороны числа и должен проверять каждый бит, пока не достигнет ненулевого бита,
while(1) { }
цикл должен будет проверять вдвое больше битов на итерацию, чемwhile(2) { }
цикл.Это требует очень неправильной ментальной модели работы компьютеров, но у нее есть своя внутренняя логика. Одним из способов проверки было бы спросить, будет ли
while(-1) { }
илиwhile(3) { }
будет одинаково быстро, или еслиwhile(32) { }
будет еще медленнее .источник
cmp
алгоритм ЦП представляет собой линейную проверку битов с ранним выходом из цикла при первом разнице.-O0
вывод gcc или clang недостаточно буквален» . Я никогда не говорил таких вещей, и я вообще не имел в виду gcc или лязг. Вы, должно быть, неправильно меня понимаете. Я просто не считаю, что то, что производит MSVC, смешно.while(1)
не прерывается перед циклом, а выражением. Но он все еще разрушается внутри цикла при оптимизации выражения. Возьми этот код с большим количеством перерывов. В неоптимизированном режиме отладки вы получаете это . При оптимизации многие точки останова падают вместе или даже перетекают в следующую функцию (в среде IDE отображаются точки останова во время отладки) - соответствующая разборка .Конечно, я не знаю реальных намерений этого менеджера, но я предлагаю совершенно другую точку зрения: при найме нового члена в команду полезно знать, как он реагирует на конфликтные ситуации.
Они загнали вас в конфликт. Если это правда, они умные и вопрос был хороший. В некоторых отраслях, таких как банковское дело, публикация вашей проблемы в Stack Overflow может стать причиной отклонения.
Но, конечно, я не знаю, я просто предлагаю один вариант.
источник
Я думаю, что ключ к этому можно найти в «вопросе старшего менеджера». Этот человек, очевидно, прекратил программировать, когда стал менеджером, а затем ему потребовалось несколько лет, чтобы стать старшим менеджером. Никогда не терял интереса к программированию, но никогда не писал строки с тех дней. Таким образом, он упоминает не «какой-нибудь достойный компилятор», как упоминают некоторые ответы, а «компилятор, с которым этот человек работал 20-30 лет назад».
В то время программисты тратили значительную часть своего времени, пробуя различные методы для ускорения и повышения эффективности своего кода, поскольку время ЦП «центрального миникомпьютера» было таким ценным. Как и люди, пишущие компиляторы. Я предполагаю, что единственный компилятор, который его компания сделала доступным в то время, был оптимизирован на основе «часто встречающихся утверждений, которые можно оптимизировать», и он столкнулся с некоторой краткостью, когда столкнулся с while (1) и оценил все еще, включая время (2). Наличие такого опыта может объяснить его позицию и его уверенность в этом.
Вероятно, лучший способ нанять вас - это способ, который позволит старшему менеджеру увлечься и дать вам 2-3 минуты лекций о «старых добрых днях программирования», прежде чем ВЫ плавно поведете его к следующему предмету интервью. (Хорошее время важно здесь - слишком быстро, и вы прерываете историю - слишком медленно, и вы помечены как человек с недостаточным вниманием). Скажите ему в конце интервью, что вам будет очень интересно узнать больше об этой теме.
источник
Вы должны были спросить его, как он пришел к такому выводу. Под любым приличным компилятором эти два компилируются с одинаковыми инструкциями asm. Итак, он должен был сказать вам компилятор, чтобы начать. И даже в этом случае вам придется очень хорошо знать компилятор и платформу, чтобы сделать теоретическое предположение. И в конце концов, на практике это не имеет значения, поскольку существуют другие внешние факторы, такие как фрагментация памяти или загрузка системы, которые будут влиять на цикл больше, чем эта деталь.
источник
Ради этого вопроса я должен добавить, что я помню, как Дуг Гвин из C Committee писал, что некоторые ранние компиляторы C без прохода оптимизатора генерировали бы тест в сборке для
while(1)
(по сравнению сfor(;;)
которым его не будет).Я бы ответил интервьюеру, сделав эту историческую заметку, а затем сказал бы, что даже если бы я был очень удивлен, любой компилятор сделал бы это, компилятор мог бы иметь:
while(1)
иwhile(2)
while(1)
потому что они рассматриваются как идиоматические. Это оставило быwhile(2)
с тестом и, следовательно, делает разницу в производительности между ними.Конечно, я бы добавил к интервьюеру, что неучтение
while(1)
иwhile(2)
одна и та же конструкция является признаком некачественной оптимизации, поскольку это эквивалентные конструкции.источник
Другой вариант такого вопроса - посмотреть, хватит ли у вас смелости сказать своему менеджеру, что он / она ошибается! И как тихо ты можешь это передать.
Моим первым инстинктом было бы генерировать выходные данные сборки, чтобы показать менеджеру, что любой порядочный компилятор должен позаботиться об этом, и если он этого не делает, вы отправите следующий патч для него :)
источник
То, как многие люди вникают в эту проблему, показывает, почему это вполне может быть тестом, чтобы увидеть, как быстро вы хотите микрооптимизировать вещи.
Мой ответ будет; это не имеет большого значения, я скорее сосредотачиваюсь на бизнес-проблеме, которую мы решаем. В конце концов, за это мне и заплатят.
Кроме того, я бы выбрал,
while(1) {}
потому что это более распространено, и другим товарищам по команде не нужно было бы тратить время, чтобы выяснить, почему кто-то выбрал бы большее число, чем 1.Теперь иди написать код. ;-)
источник
Если вы беспокоитесь об оптимизации, вы должны использовать
потому что это не имеет никаких испытаний. (циничный режим)
источник
while(2)
, оно оставляет место для оптимизации, а затем вы просто переключаетесьfor (;;)
на режим, когда будете готовы к повышению производительности.Мне кажется, это один из тех вопросов по поведенческому интервью, замаскированный под технический вопрос. Некоторые компании делают это - они задают технический вопрос, на который должен ответить любой компетентный программист, но когда интервьюируемый дает правильный ответ, интервьюер скажет им, что они не правы.
Компания хочет увидеть, как вы отреагируете в этой ситуации. Сидите ли вы тихо и не настаиваете, что ваш ответ правильный, из-за неуверенности в себе или страха расстроить интервьюера? Или вы готовы бросить вызов авторитету, который, как вы знаете, неправ? Они хотят узнать, готовы ли вы отстаивать свои убеждения и можете ли вы сделать это тактично и уважительно.
источник
Раньше я программировал C и ассемблерный код, когда такая чепуха могла иметь значение. Когда это имело значение, мы написали это на Ассамблее.
Если бы мне задали этот вопрос, я бы повторил знаменитую цитату Дональда Кнута 1974 года о преждевременной оптимизации и пошел бы, если бы интервьюер не смеялся и не шел дальше.
источник
Может быть, интервьюер намеренно задал такой тупой вопрос и попросил вас сделать 3 замечания:
источник
Вот проблема: если вы на самом деле пишете программу и измеряете ее скорость, скорость обоих циклов может быть разной! Для некоторого разумного сравнения:
с добавлением некоторого кода, который печатает время, некоторый случайный эффект, например, расположение цикла в одной или двух строках кэша, может иметь значение. По чистой случайности один цикл может полностью находиться в одной строке кэша или в начале строки кэша, или он может охватывать две строки кэша. И в результате, то, что, по словам интервьюера, является самым быстрым, на самом деле может быть самым быстрым - по совпадению.
В худшем случае: оптимизирующий компилятор не выясняет, что делает цикл, но выясняет, что значения, полученные при выполнении второго цикла, те же, что и в первом. И сгенерируйте полный код для первого цикла, но не для второго.
источник
Они оба равны - одинаковы.
Согласно спецификациям все, что не равно 0, считается истинным, поэтому даже без какой-либо оптимизации, и хороший компилятор не будет генерировать код для while (1) или while (2). Компилятор сгенерирует простую проверку
!= 0
.источник
Судя по количеству времени и усилий, которые люди потратили на тестирование, доказательство и ответ на этот очень прямой вопрос, я бы сказал, что оба были сделаны очень медленно, задавая вопрос.
И чтобы тратить на это еще больше времени ...
«Хотя (2)» смешно, потому что,
«while (1)» и «while (true)» исторически используются для создания бесконечного цикла, который ожидает, что «break» будет вызван на некотором этапе внутри цикла на основе условия, которое непременно произойдет.
«1» просто для того, чтобы всегда принимать значение «истина», и поэтому сказать «while (2)» почти так же глупо, как сказать «while (1 + 1 == 2)», которое также будет принимать значение «истина».
И если вы хотите быть полностью глупым, просто используйте: -
Я хотел бы думать, что ваш кодер сделал опечатку, которая не повлияла на выполнение кода, но если он намеренно использовал «2» просто для того, чтобы быть странным, то уволите его, прежде чем он пропустит странный шут по всему вашему коду, затрудняя читать и работать с.
источник
Это зависит от компилятора.
Если он оптимизирует код или если он оценивает 1 и 2 как true с тем же числом команд для определенного набора команд, скорость выполнения будет одинаковой.
В реальных случаях это всегда будет одинаково быстро, но было бы возможно представить конкретный компилятор и конкретную систему, когда это будет оцениваться по-разному.
Я имею в виду: это не вопрос, связанный с языком (С).
источник
Поскольку люди, желающие ответить на этот вопрос, хотят самый быстрый цикл, я бы ответил, что оба они одинаково компилируются в один и тот же код сборки, как указано в других ответах. Тем не менее, вы можете предложить интервьюеру использовать «развертывание цикла»; цикл do {} вместо цикла while.
Осторожно: вам нужно убедиться, что цикл по крайней мере всегда будет запускаться один раз .
Цикл должен иметь условие разрыва внутри.
Также для такого цикла я лично предпочел бы использовать do {} while (42), так как любое целое число, кроме 0, сделало бы эту работу.
источник
Очевидный ответ: после публикации оба фрагмента будут выполнять одинаково загруженный бесконечный цикл, что делает программу бесконечно медленной .
Хотя переопределение ключевых слов C в качестве макросов технически могло бы иметь неопределенное поведение, это единственный способ, которым я могу придумать, чтобы любой фрагмент кода был быстрым вообще: вы можете добавить эту строку над двумя фрагментами:
это действительно сделает
while(1)
вдвое быстрее (или вдвое медленнее), чемwhile(2)
.источник
Единственная причина, по которой я могу думать, почему это
while(2)
будет медленнее, это:Код оптимизирует цикл до
cmp eax, 2
Когда происходит вычитание, вы по существу вычитаете
а.
00000000 - 00000010 cmp eax, 2
вместо
б.
00000000 - 00000001 cmp eax, 1
cmp
только устанавливает флаги и не устанавливает результат. Итак, по наименее значимым битам мы знаем, нужно ли нам брать или нет с b . В то время как с вы должны выполнить два вычитания , прежде чем получить заем.источник