Быстрее ли объединять строки в вызове print() или вызывать print() несколько раз?

#java #string-concatenation

#java #строка-конкатенация

Вопрос:

Рассмотрим следующие два фрагмента кода:

 System.out.print(i   " ");
 

или

 System.out.print(i);
System.out.print(" ");
 

Хотя оба варианта в конечном итоге будут печатать один и тот же результат, какой вариант будет быстрее с точки зрения скорости выполнения?

В некотором контексте причина, по которой я спрашиваю, заключается в том, что преобразование первого во второй привело к ошибке «превышен лимит времени (TLE)» в моем онлайн-компиляторе. Это говорит о том, что первый вариант быстрее, но я не знаю, верно ли это повсеместно. Я также хотел бы понять, почему.

Комментарии:

1. «просто преобразуя первый во второй, я получил TLE» разве это не отвечает на ваш вопрос «Что занимает меньше времени»?

2. но если кто-нибудь может дать мне объяснение

3. мой код был решением вопроса в конкурсе, и я думаю, что он не имел ничего общего с этим вопросом

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

5. Вызов System.out.print обычно включает синхронизацию, чтобы избежать чередования вызовов из отдельных потоков. Похоже, что временные затраты на два вызова метода больше, чем временные затраты на конкатенацию (которая не будет синхронизирована) и один вызов метода.

Ответ №1:

Вы не можете знать наверняка — это зависит от факторов, не очевидных в вашем коде.

Когда i " " медленнее?

Чтобы это было медленнее, i должна быть очень большая строка (или какой-то объект, toString() метод которого возвращает очень большую строку, что приводит к i " " относительной дороговизне операции. Это все еще полностью в памяти, поэтому по сравнению с большинством операций ввода-вывода это незначительно, но сделайте это строкой из миллиона символов или около того, и это, вероятно, приведет к замедлению работы.

Когда i , тогда " " медленнее?

System.in буферизуется или нет. Вы не получаете никаких гарантий. Обычно это значительная цепочка систем, связанных вместе, и в конечном итоге вы заканчиваете запись в файл ( java -cp . yourpkg.YourClass >output.txt например, если вы запускали) или консоль, и если эта консоль работает через ssh, TCP / IP пакеты по сети и так далее.

Многие из этих систем добавляют массу накладных расходов, потому что это так называемые системы «пакетирования»: они вообще не могут отправлять или обрабатывать отдельные байты. Они могут обрабатывать только значительные фрагменты. Возьмем сеть: чтобы отправить некоторые данные с одного компьютера в Интернете на другой, вы упаковываете данные, которые хотите отправить, в так называемый пакет, который включает в себя много байтов данных, которые используются для систем, которые обрабатывают эти данные, чтобы знать, что с ними делать. Эти данные будут проходить через вашу сетевую карту, ваш маршрутизатор, крошечный шкафчик в конце улицы, более крупный сетевой узел в вашем городе, к главной распределительной магистрали, по оптоволокну на другую сторону океана, а затем все это обратно на сервер, который вы ssh использовали.-отредактировано. Должно быть очевидно, что вам нужно довольно много байтов информации, чтобы это произошло.

Итак, если вы хотите отправить только Hello и ничего больше, тогда создается весь пакет. Он мог бы содержать, возможно, 1800 байт данных, но он будет содержать только 5. однако у него все еще есть ~ 100 байт накладных расходов для информации о маршрутизации. Итак, общий размер пакета Hello составляет около 105 байт. Ваша сетевая карта шунтирует это на ваш маршрутизатор, и он отключается от всего мира, а затем ваш код почти сразу говорит: хорошо, отлично, теперь отправьте пробел! — и это приводит к тому, что ваша система послушно создает другой пакет, применяя все эти накладные расходы на маршрутизацию, и отправляется 101-байтовый пакет, в общей сложности 2 отдельных пакета общим объемом 206 байт.

В отличие от отправки Hello за один раз, для одного пакета из 106 байт.

Это пример того, почему обычно «буферизация» или, в противном случае, объединение ваших отправлений в меньшее количество фактических операций записи будет быстрее. Но проблема в том, что вы понятия не имеете, куда System.out идти. Консоль? Сеть? Файл? bitbucket? Кто знает. Если вы запускаете java -jar yourapp.jar >/dev/null , System.out это невероятно быстро (поскольку данные абсолютно никуда не уходят). В вашем вопросе не упоминается, к чему это приводит.

ПРИМЕЧАНИЕ: файлы в конечном итоге также основаны на пакетах, современные твердотельные накопители не могут записывать отдельные байты на диск, только целые куски за один раз. Если вы сначала напишете «привет», SSD в конечном итоге считывает весь фрагмент в память, затем обновляет некоторые байты, чтобы они прочитали «привет», затем перепрошивает местоположение на диске с помощью скачка мощности, который сбрасывает весь этот сегмент, а затем записывает весь фрагмент размером во много тысяч байт обратно. Если вы затем запишете пробел, вся эта процедура будет выполнена во второй раз, тогда как если вы вызовете write только один раз, диск, скорее всего, выполнит эту процедуру «загрузить весь фрагмент, обновить данные, увеличить фрагмент, сохранить фрагмент» только один раз.

Хорошо, так вы можете упростить это?

Ну, нет. В этом суть. Однако, как правило, это не имеет значения, и они одинаково быстры. Но, если это имеет значение, весьма вероятно, что i " " это будет быстрее.