|
Программирование >> Оптимизация возвращаемого значения
DynArraY<double> а; Допустим только индекс а[0] . а [22] =3.5; Вызывается new для расширения хранилища а посредством индекса 44; логический размер а достигает 23. а[32] = 0; Логический размер а изменяется для учета а[32] , но при этом new не вызывается. Если придется снова увеличить размер а, то ожидаемое увеличение также будет недорогим , но при условии, что новый максимальный индекс не превысит 44. Через весь этот раздел красной нитью проходит одна мысль: в большинстве случаев увеличение скорости выполнения профаммы может быть достигнуто ценой увеличения расхода памяти. Отслеживание текущих минимальных, максимальных и средних значений требует дополнительного пространства, но экономит время. Хранение результатов в кэше использует больше памяти, но сокращает время, результаты не вычисляются, а восстанавливаются из кэша. Упреждающая выборка требует места для размещения данных, которые были предварительно выбраны из памяти, но экономит время на то, чтобы пол5ить доступ к этим данным. Эта история столь же стара, как информатика: пространство часто приносится в жертву времени. (Однако не всегда. Использование больших объектов означает, что на странице виртуальной памяти или кэша поместится меньше объектов. В редких сл5аях увеличение объектов уменьшает производительность профаммы, потому что возрастает число обращений к файлу подкачки, понижается частота успешных обращений к кэшу или происходит и то, и другое. Как же выяснить, не столкнулись ли вы с аналогичной проблемой? Используйте отладчик снова и снова (см. правило 16).) Совет, который я предлагаю в этом разделе, таков: снижайте затраты на ожидаемые вычисления с помощью сверхэнергичных стратегий, то есть кэширования и упреждающей выборки данных. Это не противоречит использованию отложенного вычисления, о котором говорилось в правиле 17. Отложенное вычисление - это методика, предназначенная для повышения эффективности программ при выполнении операций, результаты которых необходимы не всегда. Сверхэнергичное вычисление повышает эффективность программ при выполнении операций, результаты которых необходимы почти всегда или часто. Обе вышеупомянутые методики гораздо труднее реализовать, чем обычную методику энергичного вычисления, но их использование может существенно повысить производительность программ. Правило 19. Изучите причины возникновения временныхобъектов Общаясь друг с другом, программисты часто называют кратковременно необходимые переменные временными . Например, в следующей процедуре swap: template<class Т> void swap{T& objectl, T& object2) T temp = objectl; objectl = object2; object2 = temp; } temp - временная переменная. Однако в рамках языка С++ объект temp не является временным. Это просто локальный объект функции. Истинные временные объекты в С++ невидимы - они не появляются в исходном коде программы. Они возникают, когда создается, но не называется, статический объект. Такие неименованные объекты обычно возникают в двух случаях: во-первых, когда для успешного вызова функций применяются неявные преобразования типов, и во-вторых, когда функции возвращают объекты. Важно понимать, как и почему эти временные объекты создаются и уничтожаются, потому что затраты на их создание и уничтожение могут оказать заметное влияние на производительность программы. Рассмотрим вначале первый случай, когда временные объекты создаются для успешного вызова функций. Это происходит, если тип объекта, передаваемого функции, не совпадает с типом ее параметра. Например, проанализируем функцию, которая считает число заданных символов в строке: Возвращает число символов ch в строке str. size t countChar(const strings str, char ch) ; char buffer[MAX STRING LEN]; char c; Считать символ и строку, используя функцию setw, чтобы избежать переполнения буфера при чтении строки. cin с setw(MAX STRING LEN) buffer; cout Обнаружено countChar(buffer, с) << символов << с << в строке buffer << endl; Взгляните на вызов функции countChar. Первый переданный параметр-массив char, но соответствующий параметр функции имеет тип const strings. Вызов может быть успешным, только если устранить несоответствие типов, и ваши компиляторы это сделают, создавая временный объект типа string. Данный временный объект инициализируется при помощи вызова конструктора string с параметром buffer. После этого параметр str в countChar связывается с временным объектом string. После возврата из функции countChar временный объект автоматически уничтожается. Преобразования такого рода удобны (хотя и опасны - см. правило 5). Но с точки зрения эффективности создание и уничтожение временного объекта string влечет за собой ненужные расходы. Устранить их можно двумя путями. Первый способ - изменить программу так, чтобы подобные преобразования не могли возникать. Данная стратегия описывается в правиле 5. Альтернативный путь - сделать так, чтобы преобразования были не нужны. Этому посвящено правило 21. Рассматриваемые преобразования происходят только при передаче объектов по значению или при передаче параметра ссылка на const . Они не возникают при передаче объекта параметру ссылка не на const . Проанализируем следующую функцию: void uppercasifY(strings str); Переводит все символы в str в верхний регистр. В примере с подсчетом знаков массив char можно было бы успешно передать функции countChar, но здесь попытка вызвать функцию uppercasifу с массивом char не удается: char subtleBookPlug[] = Effective С+ + ; uppercasify(subtleBookPlug); Ошибка! При этом не создается никаких временных объектов, чтобы сделать вызов успешным. Почему? Предположим, что временный объект создан. Тогда он был бы передан функции uppercasify, которая перевела бы все его символы в верхний регистр. Но фактический параметр в вызове функции - subtleBookPlug - не подвергся бы модификации; был бы изменен только временный объект string, полученный из subtleBookPlug. Конечно, это не совпадает с замыслом программиста. Программист передавал в функцию uppercasify параметр subtleBookPlug, ожидая, что переменная subtleBookPlug изменится. Неявное преобразование типов для объектов ссылка не на const позволило бы изменять временные объекты, тогда как программист ожидал модификации невременных объектов. Именно поэтому язык запрещает создание временных объектов для параметров ссылки не на const. Ссылка на параметры с атрибутом const не вызывает данной проблемы, потому что такие параметры не могут быть изменены. Временные объекты могут также создаваться, если функция возвращает объект. Например, функция operator* должна вернуть объект, который представляет собой сумму ее операндов. Если задан тип Number, то operator* для этого типа объявляется примерно так: const Number operator* (const Numbers Ihs, const Numbers rhs); Возвращаемое значение данной функции является временным, потому что оно не имеет имени: это только возвращаемое функцией значение. Следовательно, вызов operator+ каждый раз приводит к затратам на создание и удаление такого объекта. (Объяснение того, почему возвращаемое значение является const, вы найдете в правиле 6.)
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |