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

Рекомендации по измерению сложности выполнения кода

У меня есть gnarly часть кода, чья эффективность во времени я бы хотел измерить. Поскольку оценить эту сложность из самого кода сложно, я хочу поместить его в цикл и время результатов. Когда собрано достаточное количество точек данных (размер → время), я вижу, какая кривая подходит лучше всего.

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

Мой тестовый код работает нормально (начальный, не синхронизированный цикл разогрева для уменьшения времени загрузки, а затем, начиная с размера 10, масштабирование до 1000000 с шагом в 10%, повторение прогонов пока не истечет 5 секунд или закончится 5 полных запусков). Тем не менее, я пришел к этим числам с помощью угадывания.

Есть ли принятый "научный" способ масштабирования повторений и размера проблемы для достижения более быстрых и более точных графиков времени-vs-размера? Есть ли там код (или библиотеки), который может создавать леса все скучные биты, и о которых я должен был знать раньше, чем кататься? В частности, я могу думать, что при обнаружении ударов по таймингу можно было бы оправдать большее количество мер - в то время как относительно гладкие показания можно было бы просто считать "достаточно хорошими".

Edit

Я знаю классический метод вычисления сложности большого О. Он отлично работает для автономных алгоритмов с хорошей репрезентативной операцией (скажем, "сравнения" или "свопы" ). Он не работает так, как рекламируется, когда эти условия не выполняются (например: затраты времени на создание экземпляра С++ для LLVM, который является большим и сложным, и где я не знаю, какова будет соответствующая репрезентативная операция). Вот почему я рассматриваю его как черный ящик и пытаюсь измерять время снаружи, а не проверять код.

4b9b3361

Ответ 1

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

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

Если ничего не помогает, нормальный способ показать, как алгоритм масштабируется на машине, как вы писали. Но есть много вещей, которые влияют на результаты. Чтобы заметить некоторые из них:

  • Типы памяти: если ваш вход достаточно мал, чтобы вписаться в кеш L1, ваш алгоритм работает очень быстро, просто потому, что память работает быстро. Если ваш ввод становится больше, поэтому он больше не вписывается в кеш L1, он хранится в кеше L2, и если он становится еще больше, он сохраняется в ОЗУ. И каждый раз ваша программа замедляется огромным фактором (в дополнение к фактору растущего ввода). Хуже всего, когда он становится настолько большим, что алгоритм должен хранить некоторые тонкие входные данные на вашем жестком диске.
  • Многозадачность: если ваша ОС решает передать CPU другой программе, ваш алгоритм, похоже, замедляется. Это также трудно справиться.
  • Аппаратное обеспечение: в режиме большой-O каждая операция рассчитывается как 1 единица времени. Если ваш алгоритм выполняет множество операций, для которых оптимизирован ваш процессор, это также влияет на ваши измерения.
  • Программное обеспечение: программное обеспечение может влиять на ваши измерения так же, как и на аппаратном уровне. Например. если у вас много больших операций с использованием библиотеки, вы можете значительно ускорить работу программы с помощью GMP.
  • Warmup: если вы начнете измерять, сначала нужно разогреть процессор. Сначала запустите алгоритм на более крупном входе (без измерения).
  • Вводные случаи. Вы можете запускать свою программу только на некоторых выбранных или случайных сгенерированных вводах определенной длины. В большинстве случаев трудно сказать (или просто невозможно), если ввод приводит к более короткому или более продолжительному времени выполнения. Поэтому, возможно, вы испытываете неправильные примеры. Если у вас случайные входы, вы получаете более разные результаты.

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

Лучшее, что вы можете сделать, это:

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

Таким образом, вы можете увидеть, улучшили ли алгоритмы алгоритм или нет, а другие могут подтвердить ваши результаты.

О вводе:

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

Ответ 2

Я не знаю о каком-либо программном обеспечении для этого или предыдущей работе над ним. И, в сущности, я не думаю, что вы можете получить ответы на форму "O (безотносительно)", которые заслуживают доверия. Ваши измерения являются шумными, вы можете попытаться отличить nlog (n) от операций nsqrt (n), и, в отличие от чистого чистого математического анализа, все сброшенные константы все еще плавают вокруг messing с вами.

Тем не менее, процесс, который я пропустил бы, если бы захотел придумать наилучшую оценку:

  • Убедившись, что я записываю как можно больше информации в течение всего процесса, я бы использовал то, что я хотел бы измерить как можно больше входов (и размеров), прежде чем мне стало скучно. Возможно, ночь. Повторные измерения для каждого входа и размера.
  • Перетащите размер ввода во временные данные в пробную копию Eureqa и посмотрите, что всплывает.
  • Если меня не устраивает, получайте больше данных, продолжайте сгребать его в Eureqa и посмотрите, улучшается ли ситуация.
  • Предполагая, что Eureqa не дает ответа, который мне нравится, прежде чем мне станет скучно, что он будет потреблять все время моего процессора и мощность, я переключусь на байесовские методы.
  • Используя что-то вроде pymc Я попытался смоделировать данные, используя кучу вероятных функций сложности. (n, n ^ 2, n ^ 3, n ^ 3, n * log (n), n ^ 2 * log (n) n ^ 2 * log (n) ^ 2 и т.д. и т.д. и т.д.).
  • Сравните DIC (поменьше - лучше) каждой модели, ища самые лучшие.
  • Разделите самые лучшие, посмотрите места, где данные и модель не согласны.
  • Соберите больше данных вблизи разногласий. Подсчитайте модели.
  • Повторяйте 5-8 до тех пор, пока не начнете скучать.
  • Наконец, собирайте новые точки данных при больших размерах ввода, посмотрите, какая модель лучше всего прогнозирует эти точки данных.
  • Выберите, чтобы поверить, что один из них достаточно прав.

Ответ 3

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

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

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

Итак, чтобы сократить его:

  • Выберите размер для входных данных.
  • Запустить реализацию алгоритма.
  • Вычислить время выполнения среднее.
  • Если у вас есть предыдущий расчет среднего времени выполнения, сравните его с текущим временем выполнения, иначе повторите его из пункта 2.
  • Если разница больше или равна определенной (ранее определенной) пороговой величине, то повторяйте ее из пункта 2.
  • Увеличить размер входных данных.
  • Повторите попытку из пункта 2.

Сообщите мне, если что-то неясно.

Ответ 4

Предположим, вы запустили следующее в цикле. На итерации я = 0, 1, 2,.... для некоторого фиксированного n_0 > 0 и очень большого n вы выбираете функцию на 2 я + n_0 равноудаленных (до округления) точках в диапазоне 1,..., n. Затем вы выполняете либо одно из следующих действий, либо комбинацию обоих:

  • Поезд сплайна с использованием четных точек и проверка его на нечетные точки (а также наоборот). Решить итерацию достаточно, если ошибка l2 ниже некоторого порога.

  • Постройте сплайн, используя все точки, и проверьте его на значения at, скажем 2n. Опять же, решить, что итерации достаточно, если ошибка l2 ниже некоторого порога.

Точка 1. подчеркивает ошибку интерполяции, а в пункте 2. подчеркивается ошибка экстраполяции. Реально, я думаю, вы в лучшем случае сможете идентифицировать функции, описанные сплайном.


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

Ответ 5

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

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