Program kompilowany chodzi szybciej niż budowany

Program kompilowany chodzi szybciej niż budowany
BA
  • Rejestracja:około 10 lat
  • Ostatnio:ponad 3 lata
  • Postów:43
2

Witam,

tworzę sobie własną grę pisaną w języku Java z biblioteką JSwing. Podczas pisania programu nie zauważyłem żadnych problemów optymalizacyjnych, program chodzi bardzo szybko, powiedział bym, że płynnie, wszystko jest cacy tak jak ma być. Niestety nastąpiła chwila, kiedy musiałem udostępnić program osobom trzecim, przez co zostałem zmuszony do "zbudowania" programu aby nie oddawać również kodów źródłowych. No i tutaj pojawia się problem. Otóż program ten znacznie spowolnił.

Przykład? Postać porusza się po kratkach 32x32 px. Pomiędzy przejściem z jednej kratki na drugą wykonuje się animacja. Postać przesuwa się 4 razy w danym kierunku z przesunięciem o 8 px oraz zmienia się obrazek postaci imitujący jej ruch. W momencie, kiedy program jest kompilowany, cała akcja przejścia pomiędzy kratkami odbywa się w ciągu kilku milisekund. W momencie, kiedy program jest zbudowany i odpalam go z pliku .jar, przejście tej postaci pomiędzy kratkami odbywa się w ciągu (dajmy na to) pół sekundy. To jest znaczne opóźnienie.

Z czego to opóźnienie może wynikać? Czy podczas budowania programu trzeba na coś jeszcze zwrócić uwagę, np. jakieś dodatkowe logowania itd. ?

Program udostępniony osobom trzecim załączam jako załącznik. Aplikacja kompilowana była w Netbeans IDE 8.0.2, baza danych utworzona jest w SQLite

edytowany 1x, ostatnio: Badboss
jarekczek
  • Rejestracja:prawie 8 lat
  • Ostatnio:ponad 4 lata
  • Lokalizacja:Siemianowice Śląskie
  • Postów:500
0

Najlepiej spróbować uruchomić jakiś profiler. Poszukaj pod java profiler. Pewnie jest coś w ramach SDK. Profiler szybko wskaże Ci wąskie gardła. Sam jestem ciekaw, co wyjdzie. Może w pliku jar brakuje jakichś elementów, które są przy uruchamianiu ręcznym.

Inny sposób badania tego problemu to jakieś inne, pośrednie, metody uruchamiania. Np. wypakować jara (to zip) do plików i uruchamiać poleceniem java -cp .. Albo od drugiej strony - skopiować pliki klas z katalogu netbeansa do jakiegoś nowego folderu i tam odpadalć. Przy większych projektach zajęłoby to za dużo czasu, ma sens tylko w czymś prostym, bez wielu dependency.

Skoro jedna operacja zajmuje 0,5 sekundy, to nawet zwykły debuger powinien wskazać problematyczne miejsce. Albo logowanie na stdout.


Przeważnie ignoruję niezarejestrowanych użytkowników.
0

Prawdopodobnie jest to przez różne tryby pracy JVM w tych dwóch przypadkach, a dokładniej różne tryby kompilacji JIT lub ew. rodzaje używanego garbage collectora. Podłącz się przez VisualVM w obywdu przypadkach, pooglądaj i porównaj.

Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 18 godzin
1

Zbudowany JAR to ZIP z włączoną kompresją. Jeśli twój program bardzo często wczytuje zasoby w kółko to znaczy, że pliki z tego ZIPa będą musiały być w kółko rozpakowywane. Spróbuj zrobić jakiś cache dla zasobów, obojętne czy w pamięci czy np na dysku w jakimś katalogu tymczasowym. Ewentualnie spróbuj wyłączyć kompresję dla tego JARa.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
jarekczek
W kółko rozpakowywane? Że niby JVM nie keszuje zasobów?
Wibowit
Keszowanie interpretowanego bajtkodu to zupełnie co innego niż keszowanie zasobów w ogólności.
jarekczek
Ja mówiłem o zasobach. Ale okazuje się, że standardowo nie są keszowane. Choć niektóre classloadery mogą to robić, np. tomcatowy.
Wibowit
A Tomcat przypadkiem nie rozpakowuje sobie WARa?
0

Jak nie oddałeś kodu, jak oddałeś, c# i java są w pełni dekompilowalne, c/c++ są dekompilowalne też ale nie w 100% tzn. kod robi to samo, ale nie koniecznie jest tak samo zrobiony jak jego autor to zrobił, ale robi to samo. Chodź w sumie to samo można w javie zrobić.
Taki problem że większość czynności można na wiele sposobów wykonać.

Obfuskacja jest bardziej skuteczna bezpośrednio na procesorze.
Z logicznego punktu widzenia jest trudniejsza do analizy.

Budowę traktujesz jak plik skompresowany, ten plik raz jest rozpakowany i potem czas wykonywania jest taki sam.
Jak pamięć na takich urządzeniach jak android jest czymś świętym, to trzeba o to dbać.
Jednak tam pamięci zawsze braknie i nawet lepiej jak coś się dłużej odpala, ale działa tak samo, a zajmuje mniej pamięci.

jarekczek
Panie Mądralski, jesteś mistrzem słowa pisanego. To po prostu poezja. Przeczytałem 2 razy i nic nie rozumiem.
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 18 godzin
3

Zrobiłem testa i rzeczywiście wychodzi na to, że czytanie zasobów ze spakowanego JARa jest znacznie wolniejsze.

Jako plik testowy posłużył mi English text z http://www.maximumcompression.com/data/files/index.html

Kod testowy:

Kopiuj
package fgd;

import java.io.IOException;
import java.io.InputStream;

/**
 * Hello world!
 */
public class App {
    public static void main(String[] args) throws IOException {
        long startTime = System.currentTimeMillis();
        byte[] buffer = new byte[4096];
        for (int i = 0; i < 10; i++) {
            long startIterationTime = System.currentTimeMillis();
            InputStream stream = App.class.getResourceAsStream("/world95.txt");
            while (stream.read(buffer) >= 0) ;
            long totalIterationTime = System.currentTimeMillis() - startIterationTime;
            System.out.println("time taken for iteration: " + totalIterationTime + "ms");
        }
        long totalTime = System.currentTimeMillis() - startTime;
        System.out.println("time taken: " + totalTime + "ms");
    }
}

Odpalony wprost z IntelliJa daje taki wynik:

Kopiuj
time taken for iteration: 8ms
time taken for iteration: 2ms
time taken for iteration: 1ms
time taken for iteration: 1ms
time taken for iteration: 1ms
time taken for iteration: 0ms
time taken for iteration: 1ms
time taken for iteration: 5ms
time taken for iteration: 1ms
time taken for iteration: 1ms
time taken: 22ms

Odpalony JAR z linii komend daje taki wynik:

Kopiuj
time taken for iteration: 13ms
time taken for iteration: 11ms
time taken for iteration: 11ms
time taken for iteration: 10ms
time taken for iteration: 11ms
time taken for iteration: 10ms
time taken for iteration: 11ms
time taken for iteration: 11ms
time taken for iteration: 10ms
time taken for iteration: 11ms
time taken: 109ms

Spróbowałem jeszcze wyłączyć kompresję JARa za pomocą porad tutaj: https://stackoverflow.com/questions/1032685/how-to-turn-off-jar-compression-in-maven
Wyniki:

Kopiuj
time taken for iteration: 3ms
time taken for iteration: 1ms
time taken for iteration: 1ms
time taken for iteration: 0ms
time taken for iteration: 1ms
time taken for iteration: 1ms
time taken for iteration: 0ms
time taken for iteration: 1ms
time taken for iteration: 0ms
time taken for iteration: 1ms
time taken: 9ms

Podsumowując:

  • najszybciej z nieskompresowanego JARa: 9ms
  • nieco wolniej prosto z dysku: 22ms
  • bardzo wolno ze skompresowanego JARa: 109ms

Rozwiązaniem na krótką metę jest więc wyłączenie kompresji JARa. Możesz go skompresować po zbudowaniu, np wrzucając go wprost do pliku ZIP, RAR, 7z czy cokolowiek.
Rozwiązaniem na dłuższą metę byłoby poprawne zarządzanie zasobami, a nie wczytywanie ich w kółko. Prawdopodobnie nie dość że w kółko wczytujesz strumienie to jeszcze w kółko tworzysz jakieś obiekty Bitmap, Image czy tym podobne. Wstaw sobie licznik do kodu by sprawdzić ile razy w rzeczywistości wczytujesz zasoby i uzmysłowić sobie skalę problemu.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 1x, ostatnio: Wibowit
jarekczek
  • Rejestracja:prawie 8 lat
  • Ostatnio:ponad 4 lata
  • Lokalizacja:Siemianowice Śląskie
  • Postów:500
0

Jako ciekawostka. Można jeszcze zasoby ładować z plików, a nie z jara. Dokładny opis eksperymentu: http://todayguesswhat.blogspot.com/2011/03/jar-manifestmf-class-path-referencing.html

U mnie działa Class-Path: zasoby/


Przeważnie ignoruję niezarejestrowanych użytkowników.
edytowany 2x, ostatnio: jarekczek
BA
  • Rejestracja:około 10 lat
  • Ostatnio:ponad 3 lata
  • Postów:43
0

Póki co to kombinuję nad tym Mavenem. W Netbeans IDE kompresja jest wyłączona, a mimo to nie widzę różnicy. Jako, że Maven jest pisany specjalnie pod Javę, to sprawdzam na nim czy będzie lepiej.

Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 18 godzin
0

Możesz też ręcznie rozpakować JARa i złożyć go ponownie np ZIPem z wyłączoną kompresją.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
BA
  • Rejestracja:około 10 lat
  • Ostatnio:ponad 3 lata
  • Postów:43
0

Dzięki za pomoc Jarekczek oraz Wibowit :) Niestety efektów wam nie przedstawię, ponieważ dostałem grubszy temat do napisania i nie mam za bardzo czasu teraz tego testować. Nie ma chyba sensu trzymać tego wątku otwartego, chociaż by ze względu na tak głęboką analizę wykonaną przez Wibowita pokazującą na czym może polegać problem :)

Także temat można zakończyć.

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)