Проведение соревнования с самым быстрым алгоритмом - PullRequest
1 голос
/ 14 ноября 2009

Я бы хотел проводить соревнования, такие как соревнования по коду, но у победителя будет самый быстрый алгоритм, а не самый маленький код.

  • Одним из справедливых способов измерения скорости алгоритма является использование нейтральной виртуальной машины, такой как Java JVM. Есть ли простой способ узнать общее количество выполненных инструкций JVM? (Если запись использует несколько потоков, то общее количество инструкций JVM будет суммироваться по всем потокам.)

Например, код

public class simple {
    public static int main(String argc[]) {
        int i;

        i = 3;
        while (i > 0) {
            i--;
        }

    return 0;
    }
}

генерирует код JVM

0:  iconst_3
1:  istore_1
2:  iload_1
3:  ifle    12
6:  iinc    1, -1
9:  goto    2
12: iconst_0
13: ireturn

и требуется (если я правильно посчитал) 18 инструкций JVM для запуска.

  • Я бы хотел, чтобы люди могли вести свои записи дома и видеть, что увидят судьи. Очевидно, что если я предоставлю информацию для программы, самым быстрым решением будет выплевывание запомненных предварительно вычисленных ответов. Есть ли способ объективно разрешить людям запускать программы дома и не видеть запомненных ответов?

  • Какие еще проблемы препятствуют проведению неформальной "самой быстрой конкуренции кода"?

Спасибо!

Ответы [ 10 ]

5 голосов
/ 15 ноября 2009

Единственное достоверное сравнение - это кратчайшее время завершения работы на обычном оборудовании. Время завершения программы полностью зависит от оборудования, иначе какой смысл тратить деньги на более мощные машины?

Самое близкое, что вы можете получить к воспроизводимым результатам, это сообщить относительную скорость, например, предоставить пример программы и отчет о программе пользователя, работающей, скажем, в 50% случаев. Программа, которая в два раза быстрее на одном ПК, вероятно, будет в два раза быстрее на другом.

В универе мы будем отправлять задания, которые будут работать против «секретных» входов, однако мы могли бы отправлять несколько раз для исправления ошибок. Мое первое представление не работало вообще, но регистрировало все входные данные. ;)

РЕДАКТИРОВАТЬ: более длинный ответ.

Рассмотрим следующую программу

public class FibMain {
    public static void main(String... args) {
        {
            long start = System.nanoTime();
            System.out.println(iteration_fib(Integer.parseInt(args[0])));
            long time = System.nanoTime() - start;
            System.out.printf("Iteration took %,d us%n", time /  1000);
        }
        {
            long start = System.nanoTime();
            System.out.println(recursive_fib(Integer.parseInt(args[0])));
            long time = System.nanoTime() - start;
            System.out.printf("Recursion took %,d us%n", time /  1000);
        }
    }

    public static long iteration_fib(int n) {
        long t1 = 1;
        long t2 = 1;
        while (n-- > 2) {
            long t = t2;
            t2 += t1;
            t1 = t;
        }
        return t2;
    }

    public static long recursive_fib(int n) {
        if (n <= 2) return 1;
        return recursive_fib(n - 1) + recursive_fib(n - 2);
    }
}

Если вы посмотрите на сгенерированный байт-код с помощью javap -c, вы увидите

public static long iteration_fib(int);
  Code:
   0:   lconst_1
   1:   lstore_1
   2:   lconst_1
   3:   lstore_3
   4:   iload_0
   5:   iinc    0, -1
   8:   iconst_2
   9:   if_icmple       25
   12:  lload_3
   13:  lstore  5
   15:  lload_3
   16:  lload_1
   17:  ladd
   18:  lstore_3
   19:  lload   5
   21:  lstore_1
   22:  goto    4
   25:  lload_3
   26:  lreturn

public static long recursive_fib(int);
  Code:
   0:   iload_0
   1:   iconst_2
   2:   if_icmpgt       7
   5:   lconst_1
   6:   lreturn
   7:   iload_0
   8:   iconst_1
   9:   isub
   10:  invokestatic    #13; //Method recursive_fib:(I)J
   13:  iload_0
   14:  iconst_2
   15:  isub
   16:  invokestatic    #13; //Method recursive_fib:(I)J
   19:  ladd
   20:  lreturn

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

Я запустил FibMain 44 на своей машине и получил следующий результат.

701408733
Iteration took 495 us
701408733
Recursion took 19,174,036 us

Это связано с тем, что время, необходимое для выполнения итерации, пропорционально n (в данном случае 44) и растет линейно, однако время, необходимое для рекурсии, пропорционально результату (в данном случае 701408733) и увеличивается экспоненциально.

Если вы введете 50 в качестве входных данных, то первое завершится за мгновение, второе займет столько времени, что мне надоело ждать.

1 голос
/ 27 ноября 2010

Вы можете составить конкуренцию нескольким онлайн-инструментам, таким как SPOJ (это бесплатно и поддерживает Java). Таким образом, у вас есть один эталонный компьютер, который измеряет время выполнения программ.

1 голос
/ 15 ноября 2009

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

1 голос
/ 14 ноября 2009

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

Что касается (3), то количество используемых инструкций JVM не обязательно является хорошим показателем скорости. Некоторые реализации могут занять больше или меньше для каждой инструкции; и я даже не начал о джиттинге и других оптимизациях.

1 голос
/ 14 ноября 2009

Для (1) почему бы не просто время выполнения процесса? Разработайте загадку так, чтобы фактическая обработка была на сегодняшний день наиболее доминирующим аспектом времени, а не запуска процесса, и рассчитайте ее в течение нескольких итераций, чтобы получить среднее значение.

Для (2) предоставьте выборочный ввод, но используйте альтернативный ввод для живого конкурса.

0 голосов
/ 26 ноября 2009

Я также считаю, что подсчет количества инструкций является хорошей мерой.

Единственный недостаток, который я вижу, заключается в том, что если инструкции JVM слишком мощные. Я не знаю JVC, но возможно, что есть встроенная поддержка строк. Добавление строк может привести только к одной инструкции. (Не думаю.)

Я бы просто использовал старую time команду. Это измеряет время выполнения, а не реальное время, что исключает почти все влияния фоновых процессов.

0 голосов
/ 19 ноября 2009

Почему бы не сделать VJM на шаг вперед и внедрить полноценную виртуальную машину на базе Linux? Циклы должны быть одинаковыми (я думаю, в зависимости от того, как была реализована виртуальная машина).

Например, вы можете создать виртуальную машину на базе 8088 с 256 КБ ОЗУ и 5 МБ дискового пространства под управлением MINUX. Независимо от того, «насколько быстро» выполняется код, число циклов ЦП не будет одинаковым (относительно 8088), независимо от того, был ли 8088 реализован на Pentium Dual Core или на каком-нибудь старом Power PC.

После того, как вы установили виртуальное оборудование, выбор языка может стать частью решения для конкурса «самый быстрый алгоритм».

0 голосов
/ 19 ноября 2009

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

0 голосов
/ 18 ноября 2009

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

0 голосов
/ 14 ноября 2009

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

...