Главная
Попытка заменить пчелу
Предложения советских рационализаторов
Радиоэлектронные собеседники животных
Роботехника в производстве и в быту
Тайна профессора Рентгена
Деталь сама себя обрабатывает и охлаждает
Желтый подводный робот
Ледяные корабли
Открытия и наблюдения советских ученых
Новаторская перевозка грузов
Перпетуум мобиле с Алексеем Воробьёвым-Обуховым
Пишущая машинка стенографирует и расшифровывает
Шахматная махина маэстро кэмпелена
Роторно-винтовые ледоколы
Русскому керосину - 160 лет
Спасение в воздушных просторах
Что умеют машины
|
Главная - Литература ГЛАВА 26 Методики оптимизации кода Содержание Ш 26.1. Логика 26.2. Циклы 26.3. Изменения типов данных 26.4. Выражения 26.5. Методы 26.6. Переписывание кода на низкоуровневом языке 26.7. Если что-то одно изменяется, что-то другое всегда остается постоянным Связанные темы Стратегии оптимизации кода: глава 25 Рефакторинг: глава 24 Оптимизация кода уже давно привлекает пристальное внимание программистов. Если вы решили повысить производительность и хотите сделать это на уровне кода (с учетом предупреждений, описанных в главе 25), то можете использовать целый ряд методик. Эта глава посвящена в первую очередь повышению быстродействия, но включает и несколько советов по сокращению объема кода. Производительность обычно охватывает и быстродействие, и объем кода, но при необходимости сокращения объема кода обычно лучше прибегнуть к перепроектированию классов и данных, а не к оптимизации кода. Последняя подразумевает небольшие изменения, а не изменения более крупномасштабных аспектов проектирования. В этой главе вы почти не найдете методик, настолько общих, чтобы код примеров можно было копировать прямо в другие программы. Я просто хочу проиллюстрировать ряд видов оптимизации кода, которые вы сможете приспособить к своей ситуации. Виды оптимизации, описываемые в этой главе, могут показаться похожими на виды рефакторинга из главы 24, однако помните, что рефакторинг направлен на улуч- шение внутренней структуры программы (Fowler, 1999). То, о чем мы будем говорить в этой главе, возможно, лучше называть «антирефакторингом». Эти изменения ухудшают внутреннуюю структуру программы ради повышения ее производительности. Это верно по определению. Если бы изменения не ухудшали внутреннюю структуру, мы не считали бы их видами оптимизации - мы использовали бы их по умолчанию и считали стандартными методиками кодирования. Пецекресшл еошка Оптйм»- Некоторые авторы характеризуют методики оптимизации заций кода основана на зврис- «практические правила» или приводят данные, готике {см. раадел 5.3). ворящие о том, что определенный вид оптимизации непременно обеспечит желательный результат. Однако, как вы скоро увидите, концепция «практических правил» плохо описывает оптимизацию кода. Единственным надежным практическим правилом является оценка результатов каждого вида оптимизации в конкретной среде. Так что в этой главе представлен каталог «вещей, которые стоит попробовать»: многие из них в вашей среде пользы не принесут, но некоторые на самом деле окажутся очень эффективными. 26.1. Логика Перекрестная еыдке О других Многие задачи программирования связаны с манипулиро-аспектах использования опера- ванием логикой программы. В этом разделе мы рассмотрим торов, определяющих логику эффективное использование логических выражений, программы, см. главы 14-19. Прекращение проверки сразу же после получения ответа Допустим, у вас есть выражение: if ( 5 < X ) and ( X < 10 ) then ... Как только вы определили, что х больше 5, вторую часть проверки выполнять не нужно. Некоторые языки поддерживают так называемую «сокращен-Пбрекршнаи есшка D сокра- оценку выражений», при которой компилятор генери-женйй т. также подраздел «По- РУ автоматически прекращающий проверку после HitMaHtie правил вымиспвшяяогм- получения ответа. Сокращенная оценка выполняется, напри-ческйх выражений» раздела 19.1. мер, для стандартных операторов С++ и «условных» операторов Java. Если ваш язык не поддерживает сокращенную оценку, избегайте операторов and и or, используя вместо них дополнительную логику. Для сокращенной оценки наш код следовало бы изменить так: if ( 5 < X ) then if ( X < 10 ) then ... Принцип прекращения проверки сразу по получении ответа уместен и в других случаях. В качестве примера можно привести цикл поиска. Если вы сканируете массив введенных чисел и вам нужно только узнать, присутствует ли в массиве отрицательное значение, вы могли бы проверять значения по очереди, устанав- ливая при обнаружении отрицательного числа флаг negativeFound. Вот как выглядел бы такой цикл поиска: Пример, в котором цикл продолжает выполняться даже после получения ответа (С++) negativelnputFound = false; for ( i = 0; i < count; i++ ) { if ( input[ i ] < 0 ) { negativelnputFound = true; Лучше было бы прекращать просмотр массива сразу по обнаружении отрицательного значения. Любой из следующих советов привел бы к решению проблемы. Включите в код оператор break после строки negativelnputFound = true. Если язык не поддерживает оператор break, имитируйте его при помощи оператора goto, передающего управление первой команде, расположенной после цикла. Измените цикл for на цикл while и проверяйте значение negativelnputFound вместе с проверкой того, не превысил ли счетчик цикла значение count. Измените цикл for на цикл while, поместите сигнальное значение в первый элемент массива, расположенный после последнего исходного значения, а в условии цикла while просто проверяйте, не отрицательно ли значение. По завершении цикла узнайте, относится ли индекс обнаруженного отрицательного значения к исходному массиву или превышает на 1 индекс верхней границы массива. Подробнее о сигнальных значениях см. ниже. Вот результаты использования ключевого слова break в коде С++ и Java:
Примечания: (1) Временные показатели в этой и следующих таблицах данной главы указываются в секундах, а их сравнение имеет смысл только в пределах конкретных строк кавдой из таблиц. Действительные показатели будут зависеть от компилятора, параметров компилятора и среды, в которой выполняется тестирование. (2) Большинство результатов сравнительного тестирования основано на выполнении фрагментов кода от нескольких тысяч до многих миллионов раз, что призвано устранить колебания результатов. (3) Конкретные марки и версии компиляторов не указываются. Показатели производительности во многом зависят от марки и версии компилятора. (4) Сравнение результатов тестирования фрагментов, написанных на разных языках, имеет смысл не всегда, поскольку компиляторы разных языков не всегда позволяют задать одинаковые параметры генерирования кода. (5) Фрагменты, написанные на интерпретируемых языках (РНР и Python), в большинстве случаев тестировались с использованием более чем в 100 раз меньшего числа тестов, чем фрагменты, написанные на других языках. (6) Некоторые из показателей «экономии времени» не совсем точны из-за округления «времени выполнения кода до оптимизации» и «времени выполнения оптимизированного кода». 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 [197] 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 0.0031 |