Предоставляет ли C/С++ гарантию на минимальное время выполнения?

Почему компиляторы кажутся вежливыми в отношении циклов, которые ничего не делают и не устраняют их?

Требуется ли в стандарте C цикл цикла?

Пример: следующий код:

void foo(void) {
    while(1) {
        for(int k = 0; k < 1000000000; ++k);
        printf("Foo\n");
    }
}

работает медленнее, чем этот:

void foo(void) {
    while(1) {
        for(int k = 0; k < 1000; ++k);
        printf("Foo\n");
    }
}

даже с уровнем оптимизации -O3. Я ожидал бы, что удаление пустых петель будет разрешено и, таким образом, получить одинаковую скорость для обоих кодов.

"Время затрачено" на побочный эффект, который должен быть сохранен компилятором?

Ответ 1

Нет, затраченное время не считается наблюдаемым поведением, которое должно быть защищено с помощью правила as-if:

[C++14: 1.8/5]: Соответствующая реализация, выполняющая хорошо сформированную программу, должна производить одно и то же наблюдаемое поведение, как одно из возможных исполнений соответствующего экземпляра абстрактной машины с той же программой и тем же входом. Однако, если какое-либо такое исполнение содержит операцию undefined, этот международный стандарт не требует от реализации выполнения этой программы с этим вводом (даже в отношении операций, предшествующих первой операции undefined).

[C++14: 1.5/8]: Наименьшие требования к соответствующей реализации:

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

Эти совокупности называются наблюдаемым поведением программы. [Примечание. Более строгие соответствия между абстрактной и фактической семантикой могут быть определены каждой реализацией. -end note]

Эти петли могут быть юридически оптимизированы и, действительно, есть сценарии, в которых стандарт делает намеренные попытки сделать это еще проще:

[C++14: 1.10/24]: Реализация может предполагать, что любой поток в конечном итоге выполнит одно из следующих действий:

  • кончить,
  • сделать вызов функции ввода-вывода библиотеки,
  • доступ или изменение изменчивого объекта или
  • выполнить операцию синхронизации или атомную операцию.

[Примечание. Это предназначено, чтобы разрешить преобразования компилятора, такие как удаление пустых петель, даже если завершение невозможно. -end note]

Возможно, ваш компилятор "вежлив", заметив, что цель цикла в этих программах, по-видимому, замедляет эмиссию повторного вывода текста.:)

Ответ 2

Вы не указали компилятор, но предположим, что он gcc.

gcc не удаляет пустые циклы, по крайней мере, не в соответствии с документацией . Он содержит следующий текст:

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

Однако он может удалить пустые циклы, если они "опустели" оптимизатором, то есть если цикл содержит код, который оптимизатор может перемещать за пределы цикла, а результирующий цикл пуст.

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

Ответ 3

Не существует минимального времени выполнения для исполняемого файла C или С++, поскольку время выполнения зависит от многих проблем, связанных с платформой, таких как:

  • Частота процессора.
  • Циклы часов на инструкцию.
  • Оптимизация внутреннего процессора.
  • Перерывы.
  • Набор/возможности набора процессоров.

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

Внутренняя рабочая скорость процессора изменяется. Существует единая единица измерения времени, называемая тактовым циклом. Большинство поставщиков процессоров определяют продолжительность инструкции в тактовых циклах. Это измерение может быть затруднено из-за внутренней поддержки, такой как управление кешем.

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

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

Указание минимального времени выполнения приведет к хаосу с переносимостью языка C и С++. Некоторые платформы захотят выполнить код быстрее, чем минимальное время. Другие платформы, возможно, не смогут достичь минимального времени выполнения (но они могут извлечь выгоду из языка высокого уровня, такого как C).

Кроме того, как измерить время?

Используется ли минимальное время выполнения для циклов задержки или опроса?

Ответ 4

Нет, нет гарантии: (цитата из N1570, 5.1.2.3 Выполнение программы)

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

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