В этом коде, когда я создаю объект в main
методе, а затем вызываю этот метод объектов: ff.twentyDivCount(i)
(выполняется за 16010 мс), он выполняется намного быстрее, чем его вызов с использованием этой аннотации: twentyDivCount(i)
(выполняется за 59516 мс). Конечно, когда я запускаю его, не создавая объекта, я делаю метод статическим, чтобы его можно было вызывать в main.
public class ProblemFive {
// Counts the number of numbers that the entry is evenly divisible by, as max is 20
int twentyDivCount(int a) { // Change to static int.... when using it directly
int count = 0;
for (int i = 1; i<21; i++) {
if (a % i == 0) {
count++;
}
}
return count;
}
public static void main(String[] args) {
long startT = System.currentTimeMillis();;
int start = 500000000;
int result = start;
ProblemFive ff = new ProblemFive();
for (int i = start; i > 0; i--) {
int temp = ff.twentyDivCount(i); // Faster way
// twentyDivCount(i) - slower
if (temp == 20) {
result = i;
System.out.println(result);
}
}
System.out.println(result);
long end = System.currentTimeMillis();;
System.out.println((end - startT) + " ms");
}
}
РЕДАКТИРОВАТЬ: До сих пор кажется, что разные машины дают разные результаты, но при использовании JRE 1.8. * Исходный результат, кажется, постоянно воспроизводится.
+PrintCompilation +PrintInlining
показаноОтветы:
Используя JRE 1.8.0_45, я получаю аналогичные результаты.
Изучение:
-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining
виртуальной машины показывает, что оба метода скомпилированы и встроеныmain
будет сильно отличаться, при этом метод экземпляра будет более агрессивно оптимизирован, особенно с точки зрения развертывания цикла.Затем я снова запустил ваш тест, но с другими настройками разворачивания цикла, чтобы подтвердить вышеизложенное подозрение. Я запустил ваш код с помощью:
-XX:LoopUnrollLimit=0
и оба метода работают медленно (аналогично статическому методу с параметрами по умолчанию).-XX:LoopUnrollLimit=100
и оба метода работают быстро (аналогично методу экземпляра с параметрами по умолчанию).В заключение кажется, что с настройками по умолчанию JIT точки доступа 1.8.0_45 не может развернуть цикл, когда метод статичен (хотя я не уверен, почему он так себя ведет). Другие JVM могут дать другие результаты.
источник
Просто недоказанная догадка, основанная на ответе ассилий.
JVM использует порог для развертывания цикла, который составляет примерно 70. По какой-то причине статический вызов немного больше и не развертывается.
Обновить результаты
LoopUnrollLimit
в 52 ниже, обе версии являются медленными.Это странно, поскольку я предполагал, что статический вызов просто немного больше во внутреннем представлении, а OP попал в странный случай. Но разница вроде бы около 20, что не имеет смысла.
-XX:LoopUnrollLimit=51 5400 ms NON_STATIC 5310 ms STATIC -XX:LoopUnrollLimit=52 1456 ms NON_STATIC 5305 ms STATIC -XX:LoopUnrollLimit=71 1459 ms NON_STATIC 5309 ms STATIC -XX:LoopUnrollLimit=72 1457 ms NON_STATIC 1488 ms STATIC
Для желающих поэкспериментировать может пригодиться моя версия .
источник
NON_STATIC
иSTATIC
, но мой вывод был прав. Исправлено, спасибо.Когда это выполняется в режиме отладки, номера одинаковы для экземпляра и статического случая. Это также означает, что JIT не решается скомпилировать код в машинный код в статическом случае так же, как и в случае метода экземпляра.
Почему это так? Трудно сказать; вероятно, было бы правильно, если бы это было приложение большего размера ...
источник
Я немного подправил тест и получил следующие результаты:
Выход:
Dynamic Test: 465585120 232792560 232792560 51350 ms Static Test: 465585120 232792560 232792560 52062 ms
НОТА
Пока я тестировал их по отдельности, я получил ~ 52 секунды для динамических и ~ 200 секунд для статических.
Это программа:
public class ProblemFive { // Counts the number of numbers that the entry is evenly divisible by, as max is 20 int twentyDivCount(int a) { // Change to static int.... when using it directly int count = 0; for (int i = 1; i<21; i++) { if (a % i == 0) { count++; } } return count; } static int twentyDivCount2(int a) { int count = 0; for (int i = 1; i<21; i++) { if (a % i == 0) { count++; } } return count; } public static void main(String[] args) { System.out.println("Dynamic Test: " ); dynamicTest(); System.out.println("Static Test: " ); staticTest(); } private static void staticTest() { long startT = System.currentTimeMillis();; int start = 500000000; int result = start; for (int i = start; i > 0; i--) { int temp = twentyDivCount2(i); if (temp == 20) { result = i; System.out.println(result); } } System.out.println(result); long end = System.currentTimeMillis();; System.out.println((end - startT) + " ms"); } private static void dynamicTest() { long startT = System.currentTimeMillis();; int start = 500000000; int result = start; ProblemFive ff = new ProblemFive(); for (int i = start; i > 0; i--) { int temp = ff.twentyDivCount(i); // Faster way if (temp == 20) { result = i; System.out.println(result); } } System.out.println(result); long end = System.currentTimeMillis();; System.out.println((end - startT) + " ms"); } }
Я также изменил порядок теста на:
public static void main(String[] args) { System.out.println("Static Test: " ); staticTest(); System.out.println("Dynamic Test: " ); dynamicTest(); }
И я получил вот что:
Static Test: 465585120 232792560 232792560 188945 ms Dynamic Test: 465585120 232792560 232792560 50106 ms
Как видите, если динамический вызывается перед статическим, скорость для статического резко уменьшается.
На основе этого теста:
ПРАКТИЧЕСКОЕ ПРАВИЛО:
Java: когда использовать статические методы
источник
Пожалуйста попробуйте:
public class ProblemFive { public static ProblemFive PROBLEM_FIVE = new ProblemFive(); public static void main(String[] args) { long startT = System.currentTimeMillis(); int start = 500000000; int result = start; for (int i = start; i > 0; i--) { int temp = PROBLEM_FIVE.twentyDivCount(i); // faster way // twentyDivCount(i) - slower if (temp == 20) { result = i; System.out.println(result); System.out.println((System.currentTimeMillis() - startT) + " ms"); } } System.out.println(result); long end = System.currentTimeMillis(); System.out.println((end - startT) + " ms"); } int twentyDivCount(int a) { // change to static int.... when using it directly int count = 0; for (int i = 1; i < 21; i++) { if (a % i == 0) { count++; } } return count; } }
источник