Главная - Литература

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

category = 0;

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

Пример использования таблицы вместо сложной логики (С++)

Определение таблицы categoryTable.

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

->static int categoryTable[ 2 ][ 2 ][ 2 ] = { !b!c !bc b!c be

0, 3, 2, 2, !a

1, 2, 1, 1 a

category = categoryTable[ a ][ b ][ с ];

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

Время выполнения

Время выполнения

оптимизированного

Экономия

Соотношение

Язык

кода до оптимизации

кода

времени

быстродействия

5,04

3,39

1,5:1

Visual Basic

5,21

2,60

Отложенные вычисления

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

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

Допустим, ваша программа работает с таблицей из 5000 значений, полностью генерируя ее при запуске и затем обращайтесь к ней по мере выполнения. Если программа использует только небольшую часть элементов таблицы, возможно, есть смысл вычислять их по мере надобности, а не все сразу После вычисления элемента его можно сохранить на будущее (это называется «кэшированием»).



26.2. Циклы

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

см. также гшву ia часто следует искать именно внутри циклов. Методики,

описываемые в этом разделе, помогают ускорить выполнение циклов.

Размыкание цикла

Замыканием (switching) цикла называют принятие решения внутри цикла при каждой его итерации. Если во время выполнения цикла решение не изменяется, вы можете разомкнуть (unswitch) цикл, приняв решение вне цикла. Обычно для этого нужно вывернуть цикл наизнанку, т. е. поместить циклы в условный оператор, а не условный оператор внутрь цикла. Вот пример цикла до размыкания:

Пример замкнутого цикла (С++)

for ( i = 0; i < count; i++ ) { if ( sumType SUMTYPE NET ) { netSum = netSum + amount[ i ];

else {

grossSum = grossSum + amount[ i ];

В этом фрагменте проверка if ( sumType == SUMTYPE NET) выполняется при каждой итерации, хотя ее результат остается постоянным. Вы можете ускорить выполнение этого кода, переписав его так:

Пример разомкнутого цикла (С++)

if ( sumType SUMTYPE.NET ) { for ( i = 0; i < count; i++ ) { netSum = netSum + amount[ i ];

else {

for ( i = 0; i < count; i++ ) {

grossSum = grossSum + amount[ i ];

Примечание: Этот фрагмент нарушает несколько правил хорошего программирования. Удобочитаемость и удобство сопровождения кода обычно важнее его быстродействия или размера, но темой этой главы является производительность, а для ее повышения часто нужно поступиться другими целями. Как и в предыдущей главе, здесь вы найдете примеры методик кодирования, которые в других частях этой книги не рекомендуются.



Размыкание этого цикла позволяет ускорить его выполнение примерно на 20%:

Язык

Время выполнения кода до оптимизации

Время выполнения оптимизированного кода

Экономия времени

2,81

2,27

Java

3,97

3,12

Visual Basic

2,78

2,77

<1%

Python

8,14

5,87

К сожалению, после размыкания цикла вам придется сопровождать оба цикла параллельно. Так, если переменную count потребуется заменить на clientCount, нужно будет изменить два фрагмента, что будет раздражать и вас, и всех других программистов, которым придется работать с вашим кодом.

Этот пример также иллюстрирует главную проблему оптимизации кода: результат любого отдельного вида оптимизации непредсказуем. Размыкание цикла оказалось выгодным для трех языков из четырех, но не для Visual Basic. В случае этой конкретной версии Visual Basic размыкание цикла только затруднило сопровождение кода, ничего не дав взамен. Урок очевиден: чтобы с уверенностью говорить о результатах любого вида оптимизации, вы должны их оценить. Никаких исключений.

Объединение циклов

Если два цикла работают с одним набором элементов, можно выполнить их объединение (jamming). Выгода здесь объясняется устранением затрат, связанных с выполнением дополнительного цикла. Например, на объединение претендуют следующие циклы:

Пример отдельных циклов, которые можно объединить (Visual Basic)

For i = о to employeeCount - 1

employeeName( i ) = "" Next

For i = 0 to employeeCount - 1

employeeEarnings( i ) = 0 Next

Объединение циклов обычно требует, чтобы условия циклов были одинаковы. В нашем примере оба цикла выполняются от О до employeeCount - /, поэтому мы можем их объединить:

Пример объединенного цикла (Visual Basic)

For i = о to employeeCount - 1

employeeName( i ) = ""

employeeEarnings( i ) = 0 Next

Результаты объединения циклов таковы:



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.0032