Циклы исключений Java и устаревание (или это проблема URLEncoding?)
-
05-09-2019 - |
Вопрос
Я только что написал целое аннотацию о том, как я достиг этой точки, но решил, что проще опубликовать код и оставить все как есть :)
Насколько я могу судить, производительность test3() должна быть такой же, как и test1() - единственная разница заключается в том, где перехватывается исключение (внутри вызывающего метода test1(), внутри вызываемого метода test3()).
Почему для завершения test3() регулярно требуется время где-то между test1() и test2()?
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
public class Test {
public static void main(String[] args) {
warmup();
test1(2500000); // Exception caught inside the loop
test2(2500000); // Exception caught outside the loop
test3(2500000); // Exception caught "inside" the loop, but in the URLEncoder.encode() method
}
private static void warmup() {
// Let URLEncoder do whatever startup it needs before we hit it
String encoding = System.getProperty("file.encoding");
try {
URLEncoder.encode("ignore", encoding);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void test1(int count) {
String encoding = System.getProperty("file.encoding");
long start = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
try {
URLEncoder.encode("test 1 " + i, encoding);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
System.out.println("Performed " + count + " encodings trying to catch each in " + (end - start) + "ms");
}
private static void test2(int count) {
String encoding = System.getProperty("file.encoding");
long start = System.currentTimeMillis();
try {
for (int i = 0; i < count; i++) {
URLEncoder.encode("test 2" + i, encoding);
}
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("Performed " + count + " encodings trying to catch all in " + (end - start) + "ms");
}
private static void test3(int count) {
long start = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
URLEncoder.encode("test 3 " + i);
}
long end = System.currentTimeMillis();
System.out.println("Performed " + count + " encodings with a deprecated method in " + (end - start) + "ms");
}
}
Запуск дает мне (JDK 1.6.0_13 в Windows XP) результат:
Performed 2500000 encodings trying to catch each in 4906ms
Performed 2500000 encodings trying to catch all in 2454ms
Performed 2500000 encodings with a deprecated method in 2953ms
Итак, ответы довольно близки (мы говорим о чем-то настолько тривиальном, что это не имеет значения), но мне любопытно!
Позже...
Люди предположили, что мешает оптимизация JVM - я согласен.Итак, я разбил каждый тест на отдельный класс/основной метод и каждый по отдельности.Результаты этого:
1 - Performed 2500000 encodings trying to catch each in 5016ms
1 - Performed 5000000 encodings trying to catch each in 7547ms
1 - Performed 5000000 encodings trying to catch each in 7515ms
1 - Performed 5000000 encodings trying to catch each in 7531ms
2 - Performed 2500000 encodings trying to catch all in 4719ms
2 - Performed 5000000 encodings trying to catch all in 7250ms
2 - Performed 5000000 encodings trying to catch all in 7203ms
2 - Performed 5000000 encodings trying to catch all in 7250ms
3 - Performed 2500000 encodings with a deprecated method in 5297ms
3 - Performed 5000000 encodings with a deprecated method in 8015ms
3 - Performed 5000000 encodings with a deprecated method in 8063ms
3 - Performed 5000000 encodings with a deprecated method in 8219ms
Интересные наблюдения:
- Разрыв между перехватом каждого вызова и перехватом всего, что находится за пределами цикла, сокращается в собственной JVM (я предполагаю, что оптимизация не делает все возможное в случае «все тесты в одном» из-за других итераций, которые были выполнены)
- Разрыв между try/catch на моей стороне и try/catch внутри URLEncoder.encode() теперь намного меньше (полсекунды за 5000000 итераций), но все равно сохраняется...
Решение
Запускаем в том порядке, в котором вы разместили:
Выполнено 2500000 кодировок, пытающихся поймать каждый в 34208ms
Выполнено 2500000 кодировок, пытаясь поймать все в 31708ms
Выполнено 2500000 кодировок с устаревшим методом в 30738ms
Обратный порядок:
Выполнено 2500000 кодировок с устаревший метод в 32598ms
Выполнено 2500000 кодировок пытаются поймать все в 31239ms
Выполнено 2500000 кодировок пытаясь поймать каждого в 31208ms
Поэтому я на самом деле не думаю, что вы видите то, что, по вашему мнению, вы видите (конечно, test1 не на 66% медленнее, чем test3, как показывают ваши тесты)
Другие советы
Да, у вас есть объяснение, я думаю:
3 немного медленнее, чем 1, из-за дополнительного вызова метода.
2 быстрее, чем любой из них, поскольку он не «настраивает» и не «уничтожает» байт-код, связанный с перехватом исключений, в каждом цикле.Вы можете взломать байт-код, чтобы увидеть разницу с javap — см. http://www.theserverside.com/tt/articles/article.tss?l=GuideJavaBytecode
Используете ли вы 2 или 1, зависит от того, какое поведение вы хотите, поскольку они не эквивалентны.Я бы выбрал 1 вместо 3, поскольку в этом случае вы не используете устаревший метод, который более важен, чем небольшое увеличение скорости, но, как оказалось, 1 в любом случае быстрее.
Пожалуйста, поправьте меня, но цикл for test2 выполняет только 1 шаг из-за выбрасываемого исключения, а test1 перехватывает исключение внутри цикла и выполняется 2500000 раз.
Если вы поймаете исключение вне цикла, цикл не запустится снова.Распечатайте «int i», чтобы узнать, сколько шагов сделал цикл.
Третий — самый медленный, поскольку метод делегирует вызов устаревшему методу.