Более быстрый вывод через стандартный выход в Java? - PullRequest
6 голосов
/ 05 сентября 2011

В задаче онлайн-соревнования судей я должен вывести до 50 000 строк в течение 1 секунды через стандартный вывод (в дополнение к чтению до 200 000 пар целых чисел, для которых я использовал буфер).Кажется, моя логика верна, но я продолжаю отклонять свои представления за превышение времени выполнения в 1 секунду.Я сократил свою логику кода, чтобы просто вывести постоянную строку, и она все еще превышает ограничение по времени.

Есть ли более быстрый способ вывода, чем использование System.out.println(String s) для каждой строки вывода?

Ответы [ 4 ]

7 голосов
/ 05 сентября 2011

Я бы использовал один System.out.print вызов (или наименьший, который имеет смысл, который можно выяснить с помощью бенчмаркинга), например:

String str = "line1\nline2\nline3\n ...";
System.out.print(str);

Редактировать:

    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 500000; i++) {
        sb.append(i).append("\n");
    }
    String str = sb.toString();
    long nt = System.nanoTime();
    System.out.print(str);
    nt = System.nanoTime() - nt;
    System.out.print("\nTime(ms): " + (double)nt / 1000000);

sb.toString() не является бесплатной операцией.

Вышеуказанное занимает ~ 650 мс на моем ноутбуке (500 000 вместо запрошенных 50 000).

Edit2 : с двумя другими приемами, если время заполнения имеет значение:

  • построить StringBuilder с достаточной емкостью
  • не добавляйте для каждой строки (кодниже каждый раз добавляет 200 строк, для этого используется временная шкала sb1);возможно только если каждая строка может иметь одинаковое содержание.Наслаждайтесь.

    long nt = System.nanoTime();
    StringBuilder sb1 = new StringBuilder(400);
    for (int i = 0; i < 200; i++) {
        sb1.append("l").append("\n");
    }
    String strSb1 = sb1.toString();
    
    StringBuilder sb = new StringBuilder(1000000);
    for (int i = 0; i < 2500; i++) {
        sb.append(strSb1);
    }
    
    System.out.print(sb.toString());
    nt = System.nanoTime() - nt;
    System.out.print("\nTime(ms): " + (double)nt / 1000000);
    

~ 500мс в моем случае.

4 голосов
/ 05 сентября 2011

Как отмечалось выше, решение состоит в том, чтобы создать String с помощью StringBuilder и затем распечатать строку, возвращенную из вызова toString () StringBuilder. Это может и должно быть проверено вами.

3 голосов
/ 05 сентября 2011

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

Использование StringBuffer сопряжено с затратами на увеличение буфера.

long start = System.nanoTime();
 StringBuilder sb = new StringBuilder();
for(long i=0;i<50*1000;i++)
  sb.append("Hello World!\n");
System.out.print(sb);
long time = System.nanoTime() - start;
System.err.printf("Took %d ms%n", time/1000000);

печатает

Took 30 ms.

Однако в Linux, если вы знаете, что stdout - это файл, который вы можете записать в него напрямую.

long start = System.nanoTime();
String processId = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
FileOutputStream out = new FileOutputStream("/proc/" + processId + "/fd/1");
BufferedOutputStream bos = new BufferedOutputStream(out);
final byte[] str = "Hello World!\n".getBytes();
for (long i = 0; i < 50 * 1000; i++) {
  bos.write(str);
}
bos.close();
long time = System.nanoTime() - start;
System.err.printf("Took %d ms%n", time/1000000);

печатает

Took 9 ms.

Однако вы должны быть осторожны, насколько вы оптимизируете код так, как выможет нарушить подразумеваемые правила теста и не принять его.;)

0 голосов
/ 05 сентября 2011

Это также зависит от базовой ОС.Если бы я был тобой, я бы просто записал в файл, что намного быстрее.

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

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

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

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

...