Главная страница Случайная страница КАТЕГОРИИ: АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника |
Тестирование программы
Существует три аспекта проверки программы
1. Проверка правильности удостоверяет, что программа делает в точности то, для чего она предназначена. При этом исходят из предположений: · математическая безупречность алгоритма не гарантирует правильности его перевода в программу; · отсутствие диагностических сообщений компилятора не гарантирует правильность программы; · разумный вид получаемых результатов не дает достаточной гарантии правильности программы. Как правило, проверка правильности заключается в проведении набора тестов (в том числе путем сверки с известным решением). В общем случае нельзя дать рекомендаций для проверки. Необходимо следовать принципам. · Проверку правильности программы целиком и каждого ее модуля в отдельности необходимо планировать еще на стадии разработки. Тогда же необходимо предусмотреть возможность последующего внесения изменений, чтобы избежать переделок больших программных сегментов. · Программному тестированию должны предшествовать следующие аналитические проверки: o на соответствие команд алгоритма их программной реализации; o на соответствие описания переменных и размерностей массивов требуемым значениям; o на принадлежность значений переменных и индексов массивов допустимым диапазонам при их инициализации и при каждом обращении к внутренним и внешним процедурам; o на возможность попадания программы в бесконечные циклы. · Тестированию должно подвергаться возможно большее число ветвей программы. Проверка должна продолжаться до тех пор, пока каждый оператор не будет давать правильный результат. · Тестовые данные должны выбираться из максимально широкого диапазона, в том числе и на его границах. Некоторые из них могут быть заведомо некорректными. Результаты тестирования для любого набора данных должны быть известны заранее, иначе можно получить правдоподобные, но неверные результаты. · Исправление ошибок должно производиться постепенно, чтобы установить все места в программе, на которые влияет каждая ошибка. Исправление одной ошибки не должно порождать новых ошибок. Для выявления ошибок должны быть предусмотрены все меры (в том числе и повторная проверка) и зарезервировано достаточно времени. 2. Проверка вычислительной сложности, как правило, заключается в экспериментальном анализе сложности алгоритма или сравнительном анализе нескольких алгоритмов, решающих одну и ту же задачу. При экспериментальном анализе сложности алгоритма в ходе вычислительного эксперимента обычно устанавливают взаимосвязь между временем работы, объемом обрабатываемых данных и размерностью задачи. Исходя из полученных данных, приближенно оценивают вид алгоритма (полиномиальный или экспоненциальный). При сравнительном анализе нескольких алгоритмов учитывают следующие факторы: · различие аппаратных средств, на которых проводилось тестирование, языках программирования (компиляторов) и квалификации разработчиков; без учета этого простое сравнение продолжительности работы программ бессмысленно;
Опытный программист реализует экспоненциальный алгоритм более эффективно, чем неопытный — полиномиальный.
· различие задач, на которых проводилось тестирование; целесообразно провести объединение задач;
Каждый найдет для себя наиболее благоприятный случай.
· необходимость комплексной проверки;
Две удачи из трех — еще не повод для окончательных выводов.
· использование комплексных критериев оценки программ.
Если время выполнения — единственный критерий, то предпочтение может быть отдано программе, менее эффективной с точки зрения длины кода, потребности в вычислительных ресурсах, простоты использования и т. п.
3. Проверка эффективности реализации направлена на отыскание способа заставить правильную программу работать быстрее или расходовать меньше памяти. Этот способ проверки, называемый профилированием, целесообразен только для сложных программ или для программ, которыми будут пользоваться часто. Наиболее очевидные способы оптимизации программы следующие. · Правильное использование типов данных (экономится время и, возможно, память). Дискретные типы (целочисленные, символьные и т. п.) более эффективны, чем непрерывные (вещественные). Эффективность использования типов снижается с ростом их физического размера.
В языке Pascal обработка данных типа real выполняется менее эффективно, чем типов single, double, extended.
Следует избегать неявного преобразования типов, требующего дополнительного вызова библиотечных функций.
· Правильное использование структур данных (экономится время и память). Для эффективной индексации массивов рекомендуется уменьшать число их измерений (сокращаются затраты времени на векторизацию).
Использование констант для индексации позволяет оптимизировать обращение к массиву на этапе компиляции.
При объявлении многомерных массивов его измерения рекомендуется размещать в порядке возрастания. (В этом случае минимизируется таблица векторизации, содержащая результаты расчета относительного положения элементов — последнее измерение не векторизуется.)
· Правильное использование операций (экономится время). Арифметические операции сложения и вычитания выполняются быстрее, чем умножение и деление. Операция деления менее эффективна, чем умножение. Возведение в степень – самая неэффективная операция (кроме возведения в квадрат в языке Pascal).
Операции сдвига данных дискретных типов очень эффективны, поэтому их умножение и деление на числа, кратные двум, можно заменить соответствующим количеством сдвигов влево или вправо.
Логические операции над дискретными типами выполняются очень эффективно.
Целочисленная арифметика эффективнее арифметики с плавающей точкой, поэтому ее целесообразно использовать максимально широко. Смешивать данные разных типов в операциях не рекомендуется.
Данные с плавающей точкой следует осторожно использовать в операциях непосредственного сравнения из-за ошибок их округления.
Логические выражения обрабатываются программой до получения результата, поэтому в конъюнктивных формах их целесообразно упорядочивать по убыванию вероятности получения значения False, а в дизъюнктивных формах — по убыванию вероятности получения значения True.
Пример: если в конструкции A & & B & & C языка C (эквивалент ( A) and (B) and (C) для языка Pascal ) значение A — False, то B и C не проверяются. Аналогично: если в конструкции A || B || C языка C (эквивалент ( A) or (B) or (C) для языка Pascal ) значение A — True, то B и C не проверяются. Замечание: если в конструкции f(A) & & B & & Cf(A) чаще принимает значение False, но вычисление самой функции требует существенных затрат времени, то может оказаться, что конструкция B & & C & & f(A) будет более эффективной. · Правильная организация вычислений (экономится время, но, возможно, расходуется память). Для исключения избыточных вычислений рационально использовать промежуточные переменные.
Пример: прямой расчет корней квадратного уравнения при положительном дискриминанте (операций сложения и вычитания — 4, умножения и деления — 10, вызовов функции — 2, операций присваивания — 2). x1 = (-b – sqrt(b * b – 4.0 * a * c)) / (2.0 * a); x1 = (-b + sqrt(b * b – 4.0 * a * c)) / (2.0 * a); Расчет корней квадратного уравнения при положительном дискриминанте с промежуточными переменными (операций сложения и вычитания — 5, умножения и деления — 4, вызовов функции — 1, операций присваивания — 5). a2 = a + a; c2 = c + c; D = sqrt(b * b – a2 * c2); x1 = (-b – D) / a2; x1 = (-b + D) / a2; · Правильная организация циклов (экономия времени). Она достигается следующими способами:
o сокращение числа инициализаций цикла (сокращение потерь времени на приращение и проверку счетчиков);
o упрощение циклов (сокращение или удаление вычислений из них);
· Правильное использование подпрограмм. Передача данных в процедуры через список параметров менее эффективна, чем через глобальные переменные Не всегда стоит увлекаться погоней за высокой эффективностью, так как при этом чаще всего ухудшается самодокументируемость программного кода. Очень часто экономия времени сопровождается дополнительными расходами памяти системы (и наоборот). В случае сомнительного выигрыша вряд ли стоит предпочитать его ясности и наглядности программы.
|