Предварительная оценка

Трудно заранее оценить, насколько быстрой получится программа, и вдвойне трудно оценить скорость специфических конструкций языка или машинных инструкций. Однако совсем не трудно создать модель затрат (cost model) языка или системы, которая даст, по крайней мере, общее представление о том, сколько времени занимает выполнение основных операций.

Одним из способов, часто применяемых к стандартным языкам программирования, является использование программы, которая замеряет скорость исполнения задаваемых последовательностей кода. При этом существует ряд сложностей вроде получения воспроизводимых результатов и отсечения случайных затрат времени, обусловленных конкретной средой, однако главное — наличие возможности получить некоторую информацию, хотя бы с точностью до порядка, и при этом без особых усилий. Например, у нас есть программа для создания модели затрат для С и C++ — она приблизительно оценивает затраты на исполнение отдельных выражений, помещая их в цикл, повторяющийся несколько миллионов раз, и вычисляя затем среднее время. На машине MIPS R10000 250 MHz мы получили следующие данные (время измеряется в наносекундах на операцию):




Целочисленные операции достаточно быстры, за исключением деления и взятия остатка. Операции для чисел с плавающей точкой так же быстры или даже быстрее — сюрприз для тех, кто вырос во времена, когда операции с плавающей точкой были на порядок медленнее, чем целочисленные.

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



А вот операции ввода-вывода стоят гораздо дороже шинство других библиотечных функций:


Время, показанное дош f гее и malloc, вряд ли точно соответствует их реальной производительности, поскольку освобождение памяти сразу после выделения — не самое распространенное действие.

И наконец, математические функции:



Естественно, эти цифры будут разными на разных машинах, однако общие тенденции сохранятся, так что мы можем их использовать для прикидочной оценки тех или иных конструкций, или для сравнения операций ввода-вывода с базовыми операциями, или для принятия решения о том, стоит ли переписывать выражение или использовать встраиваемую (inline) функцию.

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

Еще одна важная причина того, что показатели производительности трудно предсказать заранее, кроется в самой архитектуре компьютеров. Наличие кэш-памяти значительно изменяет скорость исполнения программ, и большая часть аппаратных ухищрений направлена на то, чтобы нивелировать разницу в быстродействии кэш-памяти и обычной памяти. Показатели частоты процессора вроде "400 MHz" дают некоторое представление о быстродействии машины, но не более того: один из наших старых компьютеров Pentium-200 работает гораздо медленнее, чем еще более старый Pentium-100, потому только, что последний имеет гораздо больший кэш второго уровня. А разные поколения процессоров — даже при одинаковом наборе команд — тратят для исполнения одной и той же операции разное количество циклов процессора.

Упражнение 7-6

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

Упражнение 7-7

Создайте модель затрат для высокоуровневых операций в C++ — таких как создание, копирование и удаление объектов классов, вызовы функций-членов класса, виртуальных функций, встраиваемых (inline) функций, библиотеки lost ream, STL. Это упражнение не имеет фиксированного завершения, так что сосредоточьтесь на небольшом репрезентативном наборе операций.

Упражнение 7-8

Повторите предыдущее упражнение для Java.