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

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

Результаты этого вида оптимизации во многом зависят от числа проверяемых значений и вероятности обнаружения отрицательного значения. В данном тесте число значений в среднем было равным 100, а отрицательные значения составляли половину всех значений.

Упорядочение тестов по частоте

Упорядочивайте тесты так, чтобы самый быстрый и чаще всего оказывающийся истинным тест выполнялся первым. Нормальные случаи следует обрабатывать первыми, а вероятность выполнения неэффективного кода должна быть низкой. Этот принцип относится к блокам case и цепочкам операторов if-tben-else.

Рассмотрим, например, оператор Select-Case, обрабатывающий символы, вводимые с клавиатуры:

Пример плохо упорядоченного логического теста (Visual Basic)

Select inputCharacter Case "+",

ProcessMathSymbol( inputCharacter ) Case "0" To "9"

ProcessDigit( inputCharacter ) Case "!", "?"

ProcessPunctuation( inputCharacter ) Case " "

ProcessSpace( inputCharacter ) Case "A" To "Z", "a" To "z"

ProcessAlpha( inputCharacter ) Case Else

ProcessError( inputCharacter ) End Select

Порядок обработки символов в этом фрагменте близок к порядку сортировки ASCII. Однако блоки case во многом похожи на большой набор операторов if-tben-else, так что если первым введенным символом будет «а», данный фрагмент проверит, является ли символ математическим символом, числом, знаком пунктуации или пробелом, и только потом определит, что это алфавитно-цифровой символ. Зная примерную вероятность ввода тех или иных символов, вы можете разместить самые вероятные случаи первыми. Вот переупорядоченные блоки case:

Пример хорошо упорядоченного логического теста (Visual Basic)

Select inputCharacter

Case "A" To "Z", "a" To "z"

ProcessAlpha( inputCharacter ) Case " "

ProcessSpace( inputCharacter ) Case "!", "?"

ProcessPunctuation( inputCharacter ) Case "0" To "9"

ProcessDigit( inputCharacter ) Case "+",



ProcessMathSyfnbol( inputCharacter ) Case Else

ProcessError( inputCharacter ) End Select

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

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

С* 0,220 0,260 -18%

Java 2,56 2,56 0%

Visual Basic 0,280 0,260 7%

Примечание: тестирование выполнено для ввода, включавшего 78% алфавитых символов, 17% пробелов и 5% знаков пунктуации.

С Visual Basic все ясно, а вот результаты тестирования кода Java и С* довольно неожиданны. Очевидно, это объясняется способом структурирования операторов switch-case в языках С* и Java: из-за необходимости перечисления всех значений по отдельности, а не в форме диапазонов, код С* и Java не выигрывает от этого вида оптимизации в отличие от кода Visual Basic. Это доказывает, что никакой из видов оптимизации не следует применять слепо: результаты будут во многом зависеть от реализации конкретных компиляторов.

Вы могли бы предположить, что для аналогичного набора операторов if-then-else компилятор Visual Basic сгенерирует похожий код. Взгляните на результаты:

Язык

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

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

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

0,630

0,330

Java

0,922

0,460

Visual Basic

1,36

1,00

Совершенно иная картина. Те же тесты на Visual Basic теперь выполняются медленнее в пять раз без оптимизации и в четыре - в случае оптимизированного кода. Это говорит о том, что для блоков case и операторов if-then-else компилятор генерирует разный код.

Результаты оптимизации операторов if-then-else более согласованны, но общая ситуация от этого не проясняется. Обе версии кода С* и Visual Basic, основанного на блоках case, выполняются быстрее, чем обе версии кода, написанного на основе if-then-else, тогда как в случае Java все наоборот. Это различие результатов наводит на мысль о третьем виде оптимизации, описанном чуть ниже.

Сравнение быстродействия похожих структур логики

Описанное выше тестирование можно выполнить и для блоков case, и для операторов if-then-else. В зависимости от среды любой из подходов может оказаться более



выгодным. Ниже данные из двух предыдущих таблиц представлены в форме, об-

легчающей сравнение быстродействия оптимизированного кода, написанного с

применением обоих подходов:

Соотношение

Язык

case

ifthen-else

Экономия времени быстродействия

0,260

0,330

-27% 1:1

Java

2,56

0,460

82% 6:1

Visual Basic

0,260

1,00

258% 1:4

Эти результаты не имеют логического объяснения. В одном из языков case гораздо лучше, чем if-tben-else, 2. в другом наоборот. В третьем языке различие относительно невелико. Можно было бы предположить, что из-за похожего синтаксиса case в С* и Java результаты тестирования этих языков также будут похожими, но на самом деле имеет место обратное.

Этот пример ясно показывает, что оптимизация кода не подчиняется ни «практическим правилам», ни «логике». Так что без оценки результатов вам не обойтись.

Замена сложных выражений на обращение к таблице

пш *..«.г* л* и. Иногда просмотр таблицы может оказаться более быстрым, пользований Шпщ mmw выполнение сложной логической цепи. Суть сложной

сложной пшт ш. ттщ Ш. цепи обычно сводится к категоризации чего-то и выполнении того или иного действия, основанного на конкретной

категории. Допустим, вы хотите присвоить чему-то номер категории на основе

принадлежности этого чего-то к группам А, В и С:


Вот как эта задача решается при помощи сложной логической цепи: Пример сложной логической цепи (С++)

if ( ( а && ! с ) 11 ( а && b && с ) ) { category = 1;

else if ( ( b && !а ) I I ( а && с && !b ) ) { category = 2;

else if ( с && !a && !b ) { category = 3;

else {



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