Skrócenie trwania GC

0

Cześć, mam program, w którym mierzę czas wykonania programu za pomocą metody System.nanoTime(). Wywołuję ją przed wykonaniem konkretnego kodu i po a następnie obliczam różnicę i dzielę tak aby otrzymać ms. W załączniku można zobaczyć rezultat. Oś pionowa to czas wykonania programu a pozioma "czas rzeczywisty". Zależy mi na tym aby jak najbardziej obciąć skoki czasu, nawet kosztem średniego czasu wykonania programu. Zmieniłem już GC na G1, MaxGCPauseMillis mam na 50ms a NewRatio=2, jednak nic to nie zmieniło. Tworzę jak najmniej obiektów, używam jak najwięcej typów prostych, nie wiem co jeszcze mogę poprawić.

1

Najstarsza i najskuteczniejsza metoda to zwalniać zasoby na bieżąco i zrezygnować z usług z GC.
Obiekty może małe ale dużo ?

1

Teorii na ten temat może być pewnie wiele. Natomiast:

  1. Nie ka nic złego w tworzeniu obiektów, które żyją krótko - każdy GC w Javie jest PPD to zoptymalizowany
  2. 50ms to tylko wskazanie, może G1 się po prostu nie wyrabia. Spróbuj ustalić większa wartość i zobacz wtedy.
  3. Wrzuć gc.loga do jakiegoś GCeasy
  4. Sprawdź wykres zajętości heapa
1

Podstawowe pytanie, ten skoki to wynikają z GC czy czegoś innego?

0
yarel napisał(a):

Podstawowe pytanie, ten skoki to wynikają z GC czy czegoś innego?

W kodzie nie mam żadnych pauz ani oczekiwania na żadne dane. Ogólnie to wygląda to tak:

before = System.nanoTime();
controlValue = pid.calculateControlValue(m.readHoldingRegisters(slaveId, 1, 1)[0]);
m.writeSingleRegister(slaveId, 0, (int) controlValue);
m.writeSingleCoil(slaveId, 0, false);
now = System.nanoTime();
result = (now - before) / 1000000;

, gdzie m jest obiektem klasy Modbus z biblioteki JLibModbus służącym do komunikacji za pomocą protokołu Modbus TCP a pid jest obiektem do obliczania wartości za pomocą dodawania odejmowania i mnożenia, nie ma tam nic skomplikowanego.

3

Java posiada dwa wbudowane GC o niskich pauzach:
https://openjdk.java.net/projects/shenandoah/
https://openjdk.java.net/projects/zgc/

W twoim przypadku jednak zamiast próbować walczyć z pauzami zaczął bym je po prostu wykrywać i ignorować wyniki testów podczas których odpalił się GC. Do wykrywania liczby cykli GC możesz użyć JMXa: https://docs.oracle.com/javas[...]t/GarbageCollectorMXBean.html W JVMie może być kilka rodzajów GCków odpalonych (np GC young i GC old). Sprawdź który rodzaj GC ma zauważalny wpływ na skoki czasowe i tylko ten rodzaj GC bierz pod uwagę przy ignorowaniu wyników testów.

Kojarzę jedno narzędzie, które chwali się wykrywaniem aktywności GC - https://scalameter.github.io/ (jest jednak napisane w Scali):

automatically eliminate noise due to JIT compilation, garbage collection or undesired heap allocation patterns

1
infantylny napisał(a):

...
W kodzie nie mam żadnych pauz ani oczekiwania na żadne dane. Ogólnie to wygląda to tak:

before = System.nanoTime();
controlValue = pid.calculateControlValue(m.readHoldingRegisters(slaveId, 1, 1)[0]);
m.writeSingleRegister(slaveId, 0, (int) controlValue);
m.writeSingleCoil(slaveId, 0, false);
now = System.nanoTime();
result = (now - before) / 1000000;

, gdzie m jest obiektem klasy Modbus z biblioteki JLibModbus służącym do komunikacji za pomocą protokołu Modbus TCP a pid jest obiektem do obliczania wartości za pomocą dodawania odejmowania i mnożenia, nie ma tam nic skomplikowanego.

Nie twierdzę, że nie jest to GC, ale sprawdzałeś jakie masz wahania na poszczególnych operacjach readHoldingRegisters/writeSingleRegister/writeSingleCoil i wiesz, że nie ma zależności między wahaniem, a wywolaniem konkretnej operacji?

1
yarel napisał(a):
infantylny napisał(a):

Nie twierdzę, że nie jest to GC, ale sprawdzałeś jakie masz wahania na poszczególnych operacjach readHoldingRegisters/writeSingleRegister/writeSingleCoil i wiesz, że nie ma zależności między wahaniem, a wywolaniem konkretnej operacji?

Szedłbym tą drogą. Spróbowałbym sprawdzić czasy zapisu i odczytu do sterownika/urządzenia które tam masz, może to jest powodem takich różnic czasowych?
Jak miałem trochę doświadczenia z urządzeniami działającymi po modbusie to czas odpowiedzi bywa różny.

Może coś takiego i wyświetlić czasy readTime, calculationTime i writeTime?

before = System.nanoTime();
int value = m.readHoldingRegisters(slaveId, 1, 1)[0];
long readTime = before - System.nanoTime();

before = System.nanoTime();
controlValue = pid.calculateControlValue(value);
long calculationTime = before - System.nanoTime();

before = System.nanoTime();
m.writeSingleRegister(slaveId, 0, (int) controlValue);
m.writeSingleCoil(slaveId, 0, false);
long writeTime = before - System.nanoTime();

Ewentualnie przeprowadzić taką symulację, tzn odseparować się od zapisu/odczytu po modbusie i sprawdzić sam czas obliczania wartości?

int[] intArray = new int[] {4,5,6,7,8, ...};

...

before = System.nanoTime();
controlValue = pid.calculateControlValue(intArray[i++]);
//m.writeSingleRegister(slaveId, 0, (int) controlValue);
//m.writeSingleCoil(slaveId, 0, false);
now = System.nanoTime();
result = (now - before) / 1000000;
1

Zawsze można też włączyć logowanie aktywności GC (są do tego przełączniki w JVMie, wystarczy poguglać) i nawet ręcznie porównać z czasami wystąpienia pików na wykresie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0