Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
kernigan_paik.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
2.91 Mб
Скачать

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

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

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

Целочисленные (int) операции

i1++ 8

i1 = i2 + i3 12

11 = i2 - i3 12

11 = i2 * i3 12

11 = i2/i3 114

11 = i2 % i3 114

Операции с float

f1 = f2 8

f1 = f2 + f3 12

f1 = f2 - f3 12

f1 = f2 * f3 11

f1 = f2 / f3 28

Операции с double

d1 = d2 8

d1 = d2 + d3 12

d1 = d2 - d3 12

d1 = d2 * d3 11

d1 = d2 / d3 58

Преобразования чисел

i1 = f1 8

f1 = i1 8

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

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

Целочисленные (int) векторные операции

v[i] = i 49

v[v[i]] = i 81

v[v[v[i]]] = i 100

Управляющие структуры

if (i == 5) i1++ 4

if (i != 5) i1++ 12

while (i < 0) i1++ 3

i1 = sum1(i2) 57

i1 = sum2(i2, i3) 58

i1 = sum3(i2, i3, i4) 54

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

Ввод/вывод

fputs(s, fp) 270

fgets(s, 9, fp) 222

fprintf(fp, "%d\n", i) 1820

fscanf(fp, "%d", &i1) 2070

Malloc

free(malloc(8)) 342

Строковые функции

strcpy(s, "0123456789") 157

i1 = strcmp(s, s) 176

i1 = strcmp(s, "3123456789") 64

Преобразования строка/число

i1 = atoi("12345") 402

sscanf("12345", "%d", &i1) 2376

sprintf(s, "%d", i) 1492

f1 = atof("123.45") 4098

sscanf("123.45", "%f", &f1) 6438

sprintf(s, "%6.2f", 123.45) 3902

Время, показанное для free и mallос, вряд ли точно соответствует их ре­альной производительности, поскольку освобождение памяти сразу после выделения — не самое распространенное действие. И наконец, математические функции:

Математические функции

i1 = randQ 135

f1 = Iog(f2) 418

f1 = exp(f2) 462

f1 = sin(f2) 514

f1 = sqrt(f2) 112

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

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

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

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

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

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

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

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

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]