Производительность распределения памяти Java (SunOS и Windows)
-
20-09-2019 - |
Вопрос
У меня есть очень простой модульный тест, который просто выделяет много строк:
public class AllocationSpeedTest extends TestCase {
public void testAllocation() throws Exception {
for (int i = 0; i < 1000; i++) {
long startTime = System.currentTimeMillis();
String a = "dummy";
for (int j = 0; j < 1000; j++) {
a += "allocation driven";
}
System.out.println(i + ": " + (System.currentTimeMillis() - startTime) + "ms " + a.length());
}
}
}
На моем ПК с Windows (Intel Core Duo, 2,2 ГГц, 2 ГБ) в среднем печатается следующее:
...
71: 47ms 17005
72: 47ms 17005
73: 46ms 17005
74: 47ms 17005
75: 47ms 17005
76: 47ms 17005
77: 47ms 17005
78: 47ms 17005
79: 47ms 17005
80: 62ms 17005
81: 47ms 17005
...
В SunOS (5.10 Generic_138888-03 sun4v sparc SUNW, SPARC-Enterprise-T5120):
...
786: 227ms 17005
787: 294ms 17005
788: 300ms 17005
789: 224ms 17005
790: 260ms 17005
791: 242ms 17005
792: 263ms 17005
793: 287ms 17005
794: 219ms 17005
795: 279ms 17005
796: 278ms 17005
797: 231ms 17005
798: 291ms 17005
799: 246ms 17005
800: 327ms 17005
...
Версия JDK — 1.4.2_18 на обеих машинах.Параметры JVM одинаковы и составляют:
–server –Xmx256m –Xms256m
Может ли кто-нибудь объяснить, почему суперсервер SUN работает медленнее?
(http://www.sun.com/servers/coolthreads/t5120/ Performance.xml)
Решение
Процессор действительно медленнее на SPARC (1,2 ГГц), и, как ответил один из инженеров Sun, T2 обычно в 3 раза медленнее для однопоточных приложений, чем современные процессоры Intel.Хотя он также заявил, что в многопоточной среде SPARC должен быть быстрее.
Я провел многопоточный тест с использованием библиотеки GroboUtils и протестировал как распределение (посредством конкатенации), так и простые вычисления ( a += j*j ) для тестирования процессора.И я получил следующие результаты:
1 thread : Intel : Calculations test : 43ms
100 threads : Intel : Calculations test : 225ms
1 thread : Intel : Allocations test : 35ms
100 threads : Intel : Allocations test : 1754ms
1 thread : SPARC : Calculations test : 197ms
100 threads : SPARC : Calculations test : 261ms
1 thread : SPARC : Allocations test : 236ms
100 threads : SPARC : Allocations test : 1517ms
SPARC демонстрирует здесь свою мощь, превосходя Intel по производительности на 100 потоках.
Вот тест многопоточного вычисления:
import java.util.ArrayList;
import java.util.List;
import net.sourceforge.groboutils.junit.v1.MultiThreadedTestRunner;
import net.sourceforge.groboutils.junit.v1.TestRunnable;
import junit.framework.TestCase;
public class TM1_CalculationSpeedTest extends TestCase {
public void testCalculation() throws Throwable {
List threads = new ArrayList();
for (int i = 0; i < 100; i++) {
threads.add(new Requester());
}
MultiThreadedTestRunner mttr = new MultiThreadedTestRunner((TestRunnable[]) threads.toArray(new TestRunnable[threads.size()]));
mttr.runTestRunnables(2 * 60 * 1000);
}
public class Requester extends TestRunnable {
public void runTest() throws Exception {
long startTime = System.currentTimeMillis();
long a = 0;
for (int j = 0; j < 10000000; j++) {
a += j * j;
}
long endTime = System.currentTimeMillis();
System.out.println(this + ": " + (endTime - startTime) + "ms " + a);
}
}
}
Вот тест многопоточного распределения:
import java.util.ArrayList;
import java.util.List;
import junit.framework.TestCase;
import net.sourceforge.groboutils.junit.v1.MultiThreadedTestRunner;
import net.sourceforge.groboutils.junit.v1.TestRunnable;
public class TM2_AllocationSpeedTest extends TestCase {
public void testAllocation() throws Throwable {
List threads = new ArrayList();
for (int i = 0; i < 100; i++) {
threads.add(new Requester());
}
MultiThreadedTestRunner mttr = new MultiThreadedTestRunner((TestRunnable[]) threads.toArray(new TestRunnable[threads.size()]));
mttr.runTestRunnables(2 * 60 * 1000);
}
public class Requester extends TestRunnable {
public void runTest() throws Exception {
long startTime = System.currentTimeMillis();
String a = "dummy";
for (int j = 0; j < 1000; j++) {
a += "allocation driven";
}
long endTime = System.currentTimeMillis();
System.out.println(this + ": " + (endTime - startTime) + "ms " + a.length());
}
}
}
Другие советы
Насколько я понимаю, машины на базе UltraSPARC T2 ориентированы на производительность на ватт, а не на чистую производительность.Вы можете попробовать разделить время выделения на потребляемую мощность и посмотреть, какие цифры вы получите.:)
Есть ли причина, по которой вы используете 1.4.2 вместо 1.6?
Аппаратное обеспечение SunOS медленнее, и виртуальная машина также может быть несколько медленнее.
Я не думаю, что это измерение распределения памяти.Начнем с того, что в системе происходит очень много копирования персонажей. a += "allocation driven";
.Но я подозреваю, что настоящим узким местом является получение результатов от System.out.println(...)
через сетевые уровни из приложения на сервере Sun на удаленную рабочую станцию.
В качестве эксперимента попробуйте умножить количество внутренних циклов на 10 и 100 и посмотреть, «ускорит» ли это работу сервера Sun по сравнению с вашей рабочей станцией.
Еще вы можете попробовать перенести внутренний цикл в отдельную процедуру.Возможно, поскольку вы выполняете всю работу за один вызов main
, JIT-компилятор никогда не сможет его скомпилировать.
(Подобные искусственные «микро-бенчмарки» всегда подвержены подобным эффектам.Я склонен им не доверять.)