Технические причины позади форматирования при постепенном увеличении 1 в 'для' цикла?

Было бы уместно также упомянуть, что располагают, не всегда относится к памяти? Я располагаю ресурсы такие ссылки на файлы чаще, чем память. GC.Collect () непосредственно касается сборщика "мусора" CLR, и можете, или не может свободная память (в Диспетчере задач). Это, вероятно, повлияет на Ваше приложение отрицательными способами (например, производительность).

В конце дня, почему Вы хотите память назад сразу? Если будет давление памяти откуда-либо, то ОС получит Вас память в большинстве случаев.

53
задан 7 revs, 4 users 51% 22 July 2015 в 00:20
поделиться

13 ответов

Всем нравятся их микрооптимизации, но, насколько я понимаю, это не имеет значения. Я скомпилировал два варианта с g ++ on для процессоров Intel без каких-либо изысканных оптимизаций, и результаты приведены для

 для (int i = 0; i <5; i ++)
    movl $0, -12(%ebp)
    jmp L2
L3:
    leal    -12(%ebp), %eax
    incl    (%eax)
L2:
    cmpl    $4, -12(%ebp)
    jle L3

 для (int i = 0; i! = 5; ++ i)
    movl    $0, -12(%ebp)
    jmp L7
L8:
    leal    -12(%ebp), %eax
    incl    (%eax)
L7:
    cmpl    $5, -12(%ebp)
    jne L8

Я думаю, что jle и jne должны переводиться в одинаково быстрые инструкции на большинстве архитектур. Так что для производительности вы не должны различать эти два параметра. В общем, я согласен с тем, что первый немного безопаснее и, я думаю, более распространен.


РЕДАКТИРОВАТЬ (2 года спустя): Поскольку эта тема недавно снова привлекла много внимания, я хотел бы добавить что на этот вопрос будет сложно ответить вообще. Какие версии кода более эффективны, в стандарте C [PDF] конкретно не определяется (и то же самое относится к C ++ и, вероятно, также к C #).

Раздел 5.1.2.3 Выполнение программы

§1 Семантические описания в этом международном стандарте описывают поведение абстрактной машины, в которой вопросы оптимизации не имеют значения.

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

Что касается вкуса, я пишу

for(int i = 0; i < 5; ++i)
86
ответ дан 7 November 2019 в 08:12
поделиться

Если бы ваш индекс не был int , а вместо этого (скажем) классом C ++, то второй пример мог бы быть более эффективен.

Однако, как написано, ваша вера в то, что вторая форма более эффективна, просто неверна. Любой достойный компилятор будет иметь отличные идиомы кодогенератора для простого цикла for и будет генерировать высококачественный код для любого примера. Ближе к делу:

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

  • Если ваш цикл for критичен к производительности и не выполняет тяжелые вычисления так что арифметика индексов действительно имеет значение, вам почти наверняка следует реструктурировать свой код, чтобы выполнять больше работы на каждом проходе цикла.

1
ответ дан 7 November 2019 в 08:12
поделиться

Ну ... это нормально, пока вы не изменяете i внутри цикла for. Настоящий «САМЫЙ ЛУЧШИЙ» синтаксис для этого полностью зависит от желаемого результата.

1
ответ дан 7 November 2019 в 08:12
поделиться

По поводу читаемости. Как программист на C #, которому нравится Ruby, я недавно написал метод расширения для int, который допускает следующий синтаксис (как в Ruby):

4.Times(x => MyAction(x));
2
ответ дан 7 November 2019 в 08:12
поделиться

Я думаю, что второй вариант менее читабелен (хотя бы потому, что "стандартной" практикой кажется первое).

2
ответ дан 7 November 2019 в 08:12
поделиться

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

18
ответ дан 7 November 2019 в 08:12
поделиться

In generic code you should prefer the version with != operator since it only requires your i to be equally-comparable, while the < version requires it to be relationally-comparable. The latter is a stronger requirement than the former. You should generally prefer to avoid stronger requrements when a weaker requirement is perfectly sufficient.

Having said that, in your specific case if int i both will work equally well and there won't be any difference in performance.

4
ответ дан 7 November 2019 в 08:12
поделиться

Форма

for (int i = 0; i < 5; i++)

- idiomatic , поэтому его легче читать опытным программистам на C. Особенно, когда используется для перебора массива. По возможности вы должны писать идиоматический код, так как он читается быстрее.

Это также немного безопаснее в ситуациях, когда вы изменяете i внутри цикла или используете приращение, отличное от 1. Но это мелочь. Лучше всего тщательно спроектировать цикл и добавить несколько утверждений, чтобы на раннем этапе отловить неверные предположения.

41
ответ дан 7 November 2019 в 08:12
поделиться

Что касается использования ++ i вместо i ++, это не имеет значения для большинства компиляторов, однако ++ i может быть более эффективным, чем i ++ при использовании в качестве итератора.

12
ответ дан 7 November 2019 в 08:12
поделиться

В ваш код добавлены числовые литералы? К стыду ...

Возвращаясь к делу, Дональд Кнут однажды сказал

Мы должны забыть о малых эффективности, говорят около 97% время: преждевременная оптимизация - это корень всего зла.

Итак, все сводится к тому, что легче анализировать

Итак ... принимая во внимание оба вышеперечисленного, что из следующего легче разобрать программисту?

for (int i = 0; i < myArray.Length; ++i)

for (int i = 0; i != myArray.Length; ++i)

Изменить: мне известно, что массивы в C # реализуют интерфейс System.Collections.IList, но это не обязательно верно для других языков.

2
ответ дан 7 November 2019 в 08:12
поделиться

Я бы никогда этого не сделал:

for(int i = 0; i != 5; ++i)

i! = 5 оставляет возможность, что мне никогда не будет 5. Слишком легко пропустить это и попасть в бесконечный цикл или ошибка доступа к массиву.

++i

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

Я думаю, что у Дугласа Крокфорда есть лучшее предложение - не использовать ++ или - вообще. Иногда это может просто сбивать с толку (может быть, не в цикле, а в других местах), и так же легко написать i = i + 1. Он думает, что это просто дурная привычка, чтобы избавиться от нее, и я вроде как согласен, увидев ужасный "оптимизированный" код.

Я думаю, что Крокфорд имеет в виду, что с этими операторами вы можете заставить людей писать такие вещи, как:

var x = 0;
var y = x++;

y = ++x * (Math.pow(++y, 2) * 3) * ++x;

alert(x * y);

// ответ 54 кстати.

4
ответ дан 7 November 2019 в 08:12
поделиться

Если по какой-то причине i перескочит на 50 в цикле, ваша версия будет повторяться бесконечно. i <5 - это проверка работоспособности.

69
ответ дан 7 November 2019 в 08:12
поделиться

Это зависит от языка.

Тексты на C ++ часто предлагают второй формат, поскольку он будет работать с итераторами, которые можно сравнивать (! =) Напрямую, но не с условием больше или меньше. Также предварительное приращение может быть быстрее, чем последующее приращение, поскольку нет необходимости в копии переменной для сравнения - однако оптимизаторы могут с этим справиться.

Для целых чисел работает любая форма. Обычная идиома для C - первая, а для C ++ - вторая.

Для C # и Java я бы использовал foreach, чтобы перебирать все вещи.

В C ++ также есть функция std :: for_each, требующая использование функтора, который для простых случаев, вероятно, более сложен, чем любой из приведенных здесь примеров, и Boost FOR_EACH, который может выглядеть как foreach C #, но сложен внутри.

13
ответ дан 7 November 2019 в 08:12
поделиться