Подтвердить что ты не робот

Предотвращение регрессий производительности в R

Каков хороший рабочий процесс для обнаружения регрессий производительности в пакетах R? В идеале я ищу что-то, что интегрируется с R CMD check, которое предупреждает меня, когда я ввел в свой код значительную регрессию производительности.

Что такое хороший рабочий процесс в целом? Какие другие языки предоставляют хорошие инструменты? Это что-то, что может быть построено на верхнем модульном тестировании, или это обычно делается отдельно?

4b9b3361

Ответ 1

Это очень сложный вопрос и тот, с которым я часто сталкиваюсь, поскольку я заменяю другой код в пакете, чтобы ускорить процесс. Иногда регрессия производительности сопровождается изменением алгоритмов или реализацией, но может также возникнуть из-за изменений в используемых структурах данных.

Каков хороший рабочий процесс для обнаружения регрессий производительности в пакетах R?

В моем случае я склонен иметь очень конкретные варианты использования, которые я пытаюсь ускорить, с разными фиксированными наборами данных. Как писал Spacedman, важно иметь фиксированную вычислительную систему, но это почти невозможно: иногда на общем компьютере могут быть другие процессы, которые замедляют работу на 10-20%, даже если он выглядит довольно бездействующим.

Мои шаги:

  • Стандартизируйте платформу (например, одну или несколько машин, конкретную виртуальную машину или виртуальную машину + конкретную инфраструктуру, типы экземпляров la Amazon EC2).
  • Стандартизируйте набор данных, который будет использоваться для тестирования скорости.
  • Создание сценариев и исправление промежуточных данных (т.е. сохранение в файлах .rdat), которые связаны с очень минимальными преобразованиями данных. Мое внимание сосредоточено на каком-то моделировании, а не на обработке данных или трансформации. Это означает, что я хочу предоставить точно такой же блок данных для функций моделирования. Если, однако, целью является преобразование данных, то убедитесь, что предварительно преобразованные/управляемые данные максимально приближены к стандарту в тестах различных версий пакета. (См. этот вопрос для примеров memoization, cacheing и т.д., Которые могут использоваться для стандартизации или ускорения нефокулярных вычислений. Он ссылается на несколько пакетов OP. )
  • Повторите тесты несколько раз.
  • Масштабировать результаты по сравнению с фиксированными критериями, например. время выполнения линейной регрессии, сортировка матрицы и т.д. Это может допускать "локальные" или переходные изменения в инфраструктуре, например, из-за ввода-вывода, системы памяти, зависимых пакетов и т.д.
  • Осмотрите профиль профилирования как можно активнее (см. этот вопрос для некоторых соображений, также ссылаясь на инструменты из OP).

    В идеале, я ищу что-то, что интегрируется с проверкой R CMD, которое предупреждает меня, когда я ввел в свой код значительную регрессию производительности.

    К сожалению, у меня нет ответа на этот вопрос.

    Что такое хороший рабочий процесс в целом?

    Для меня это очень похоже на общее тестирование динамического кода: является ли воспроизводимый, оптимальный и прозрачный вывод (время выполнения в этом случае)? Прозрачность исходит из понимания того, что влияет на общее время. Здесь важны предложения Майка Данлави, но я предпочитаю идти дальше, используя профилировщик строк.

    Что касается профилировщика строк, см. мой предыдущий вопрос, который ссылается на опции в Python и Matlab для других примеров. Это наиболее важно для изучения времени часов, но также очень важно отслеживать распределение памяти, количество раз, когда выполняется строка, и глубина стека вызовов.

    Какие другие языки предоставляют хорошие инструменты?

    Практически все другие языки имеют лучшие инструменты.:) Интерпретированные языки, такие как Python и Matlab, имеют хорошие и, возможно, знакомые примеры инструментов, которые можно адаптировать для этой цели. Хотя динамический анализ очень важен, статический анализ может помочь определить, где могут быть серьезные проблемы. Matlab имеет отличный статический анализатор, который может сообщать, например, когда объекты (например, векторы, матрицы) растут внутри петель. Страшно найти это только посредством динамического анализа - вы уже потратили время на выполнение, чтобы обнаружить что-то подобное, и это не всегда заметно, если ваш контекст выполнения довольно прост (например, несколько итераций или небольшие объекты).

    Что касается языковых агностических методов, вы можете посмотреть:

    • Valgrind и cachegrind
    • Мониторинг дискового ввода-вывода, грязных буферов и т.д.
    • Мониторинг ОЗУ (Cachegrind полезен, но вы можете просто контролировать распределение ОЗУ и множество деталей об использовании ОЗУ)
    • Использование нескольких ядер

    Это что-то, что можно построить на верхнем модульном тестировании, или это обычно делается отдельно?

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


Обновление 1: Если я могу быть таким смелым, есть еще один вопрос, который я бы рекомендовал включить, а именно: "Каковы некоторые ошибки в сравнении времени выполнения двух версий пакета?" Это аналогично предположению, что две программы, реализующие один и тот же алгоритм, должны иметь одинаковые промежуточные объекты. Это не совсем верно (см. этот вопрос - не то, что я продвигаю свои собственные вопросы, здесь - это просто тяжелая работа, чтобы сделать вещи лучше и быстрее..., ведущие к много вопросов на эту тему:)). Аналогичным образом, два исполнения одного и того же кода могут отличаться по времени, затрачиваемому за счет других факторов, отличных от реализации.

Таким образом, некоторые ошибки, которые могут возникать либо на одном языке, либо на разных языках, в одном экземпляре выполнения или в "идентичных" экземплярах, которые могут повлиять на время выполнения:

  • Сбор мусора - различные реализации или языки могут поражать сбор мусора при разных обстоятельствах. Это может привести к тому, что два исполнения будут разными, хотя они могут сильно зависеть от контекста, параметров, наборов данных и т.д. Одержимое выполнение GC будет выглядеть медленнее.
  • Кэширование на уровне диска, материнской платы (например, L1, L2, L3 кэшей) или других уровней (например, memoization). Часто первое исполнение будет выплачиваться.
  • Динамическое масштабирование напряжения - Это отстой. Когда есть проблема, это может быть одним из самых трудных зверей, которые можно найти, так как он может быстро уйти. Это похоже на кеширование, но это не так.
  • Любой менеджер приоритетов заданий, о котором вы не знаете.
  • Один метод использует несколько ядер или делает некоторые умные вещи о том, как работа распределяется между ядрами или процессорами. Например, процесс блокировки ядра может быть полезен в некоторых сценариях. Одному исполнению пакета R может быть повезло в этом отношении, другой пакет может быть очень умным...
  • Неиспользуемые переменные, чрезмерная передача данных, грязные кеши, незатухающие буферы,... список продолжается.

Ключевым результатом является: В идеале, как мы должны тестировать различия в ожидаемых значениях, при условии случайности, создаваемой из-за эффектов порядка? Ну, довольно просто: вернитесь к экспериментальному дизайну.:)

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

Ответ 2

Единственный способ сделать что-нибудь здесь - сделать некоторые предположения. Итак, допустим, что машина не изменилась, или же требуется "повторная калибровка".

Затем используйте единую тестовую аналогичную структуру, и лечение "должно выполняться в X единицах времени", как только еще один критерий тестирования, который должен быть выполнен. Другими словами, сделайте что-то вроде

 stopifnot( timingOf( someExpression ) < savedValue plus fudge)

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

Ничто из того, что Хэдли не смог обработать, поэтому я думаю, что мы можем почти ожидать новый пакет timr после следующего длительного академического перерыва:). Конечно, это должно быть либо необязательным, потому что на "неизвестной" машине (подумайте: CRAN, проверяя пакет) у нас нет ориентира, иначе фактор выдумки должен "перейти на 11", чтобы автоматически принимать на новой машине.

Ответ 3

Недавнее изменение, объявленное на канале R-devel, может дать грубую оценку этому.

ИЗМЕНЕНИЯ В УТИЛИЗАЦИЯХ R-devel

'R CMD check может факультативно сообщать о таймингах в разных частях проверки: это контролируется переменными окружения, задокументированными в "Написание расширений R".

См. http://developer.r-project.org/blosxom.cgi/R-devel/2011/12/13#n2011-12-13

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

Это не так хорошо, как поддержка времени в отдельных наборах тестов, но также не зависит от какого-либо конкретного набора тестов.