[xml/java] code generatie ipv parsing?

Pagina: 1
Acties:
  • 182 views sinds 30-01-2008
  • Reageer

  • flowerp
  • Registratie: September 2003
  • Laatst online: 04-02 02:01
Voor een web applicatie (j2ee) maak ik gebruik van XML voor de beschrijving van een state machine. Dit kan een andere zijn per pagina, maar gemiddeld blijven de bezoekers een flink tijdje op een enkele pagina.

Nu moet het document natuurlijk geparsed worden naar een interne structuur. Dit gebeurd eenmalig bij het binnenkomen in de state machine. De overhead kan oplopen tot zo'n 300ms op een 2ghz machine, maar is gemiddeld zo'n 120ms voor eerste aanvragen van een document (gemeten door simpele system time calls voor en na het parsen te doen).

Het lijkt trouwens dat veel tijd in het laden van disk zit, want na enige keren het document overnieuw te laten parsen gaan de tijden naar 20ms of zelfs 0ms.

Nu zat ik laatst een stuk te lezen over code generatie en ik dacht dat dat mischien wel handig was voor mijn XML structuur. In plaats van on-the-fly te parsen en mijn interne structuur op te bouwen kan ik ook eenmalig van te voren een class genereren vanuit mijn XML file, die ik dan via class for name inlaadt in mijn applicatie.

Nu vraag ik mij af of dit een goede strategie is. Het klinkt redelijk recht toe recht aan, maar ik hoor er eigenlijk niet zo vaak iemand over, dat je een XML file al van te voren naar een java bestand omzet en dat dan compileer.

Wat denken jullie? Zou dit een goede zet zijn of juist niet?

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 09-05 08:08

Janoz

Moderator Devschuur®

!litemod

Ik weet niet hoe je xml nu in je applicatie opgenomen, maar ik kan me voorstelen dat deze documenten aangepast kunnen worden zonder de applicatie opnieuw te deployen. Het voordeel is dan dus dat bij een wijziging van de xml niet de helle applicatie hoeft worden geherstart. Wanneer je code gaat genereren zul je, bij elke wijziging van je xml, die applicatie wel moeten herstarten.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Lees je iedere keer dat xml bestand opnieuw in? Of doe je dat eenmalig?

Sun is trouwens bezig met een JSR runtime code generatie en compilatie, alleen kan ik er niet zo snel een referentie naar vinden. *is vandaag lui*

Oplossing:
ik zou dus eenmalig parsen en een generieke statemachine structuur maken. Hierin beschrijf je det toestanden en de toestandsovergangen. Verder kun je dan een instantie maken van zo`n statemachine waarin je eigelijk alleen de huidige toestand in hoeft te beschrijven en waarin je een referentie hebt naar die de statemachine-omschrijving. Op deze manier kun je dus heel goedkoop een statemachine aanmaken.

Is het trouwens een pageflow systeem?

[ Voor 3% gewijzigd door Alarmnummer op 28-03-2005 15:00 ]


  • flowerp
  • Registratie: September 2003
  • Laatst online: 04-02 02:01
Alarmnummer schreef op maandag 28 maart 2005 @ 14:59:
Lees je iedere keer dat xml bestand opnieuw in? Of doe je dat eenmalig?
Het xml bestand wordt ingeladen door de state machine handler, en dat wordt eenmalig gedaan per "machine" per user. Zolang de user dus door dezelfde machine navigeert zal er geen xml file overnieuw worden ingeladen en geparsed. Een xml file kan in principe een schier oneindig aantal states beschrijven, maar practisch zit ik nu op een stuk of 20.

De state macine handler kan hergebruikt worden voor verschillende xml bestanden (verschillende state machine beschrijvingen). In dit geval wordt er een nieuwe xml file ingeladen en geparsed. Als de gebruiker dus in zijn navigatie vaak tussen twee machines heen en weer gaat, betaal je telkens de parse kosten. Wel merkte ik dat de totale parse tijd (inclusief wat lichte processing), afneemt als ik een paar keer heen en weer schakel tussen state machines. Omdat ik zelf niets expliciet cache, neem ik aan dat mijn os (linux) de xml bestanden cached, maar dan nog is de overhead afname best wel groot.

Een andere mogelijkheid is om vanaf de JSP pagina voor elke state machine een apart state machine handler object in de session te zetten. Omdat die dingen niet echt heel erg klein zijn en omdat dat per user gebeurd, wilde ik dat ook eigenlijk niet te veel doen. Het is wel een heel expliciete mogelijkheid in mijn design geweest.
Sun is trouwens bezig met een JSR runtime code generatie en compilatie, alleen kan ik er niet zo snel een referentie naar vinden. *is vandaag lui*
Ik dacht dat ik dit ook voorbij zag komen laatst op theserverside. Was dit niet voor de J2se 1.6? *is vandaag ook lui* ;)
Is het trouwens een pageflow systeem?
Je zou het er mee kunnen vergelijken. Maar de states hebben geen correspondentie met pagina's, maar met logische toestanden in een data-ruimte. Mischien had ik met bijvoorbeeld spring web flow ook zoiets kunnen maken, maar dan had ik voor elke state een aparte pagina moeten aanmaken wat niet echt natuurlijk is voor wat ik aan het doen ben.

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


  • flowerp
  • Registratie: September 2003
  • Laatst online: 04-02 02:01
Janoz schreef op maandag 28 maart 2005 @ 14:14:
Ik weet niet hoe je xml nu in je applicatie opgenomen, maar ik kan me voorstelen dat deze documenten aangepast kunnen worden zonder de applicatie opnieuw te deployen. Het voordeel is dan dus dat bij een wijziging van de xml niet de helle applicatie hoeft worden geherstart. Wanneer je code gaat genereren zul je, bij elke wijziging van je xml, die applicatie wel moeten herstarten.
Dat is inderdaad wel een belangrijk punt waar ik over zat na te denken. Aan de ene kant is het een heel universeel probleem in J2EE: Stop je al je code en logica ook in JSP pagina's ipv beans/pojo's omdat je die kunt aanpassen zonder de server te herstarten?

De echte techneut zal er van gruwelen, maar een manager van een project waar ik ooit opzat wilde echt dat we alle logica in JSPs zetten omdat "de server herstarten gewoon niet kan, en er altijd aanpassingen gemaakt moeten worden".

Waar ik wel aan zat te denken, mocht ik de code generatie gaan doen, is om gewoon beide mogelijkheden te houden. XML files kunnen dan 'compiled' worden, maar dan is er een server herstart nodig, wil men dat niet dan kan per situatie alsnog geparsed worden.

Wat ook nog een mogelijkheid is:

Ik maak een statemachine loader die de concrete classes in laadt. Via een synchronized static map maakt deze een mapping naar een daarwerkelijke classe naam. Als er nu een nieuwe versie van een gecompileerd XML bestand komt (een nieuwe classe naam dus), dan maak ik via een admin interface een nieuwe mapping aan. Nieuwe clients krijgen dan automatisch de nieuwe class. Het is nog maar een idee'tje en moet er eigenlijk nog een beetje beter over nadenken :)

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

flowerp schreef op maandag 28 maart 2005 @ 16:35:
[...]
Het xml bestand wordt ingeladen door de state machine handler, en dat wordt eenmalig gedaan per "machine" per user. Zolang de user dus door dezelfde machine navigeert zal er geen xml file overnieuw worden ingeladen en geparsed. Een xml file kan in principe een schier oneindig aantal states beschrijven, maar practisch zit ik nu op een stuk of 20.
Uhh.. je kunt toch zelf wel nagaan dat je een performance winst kan halen door het inlezen eenmalig te latengebeuren?

Dus 1 keer per statemachine en verder niet weer. Alle gebruikers delen 1 instantie van een statemachine beschrijving.. Maar hebben hun eigen statemachine (die een ref heeft naar die beschrijving en de huidige state).
De state macine handler kan hergebruikt worden voor verschillende xml bestanden (verschillende state machine beschrijvingen). In dit geval wordt er een nieuwe xml file ingeladen en geparsed. Als de gebruiker dus in zijn navigatie vaak tussen twee machines heen en weer gaat, betaal je telkens de parse kosten.
Daarom moet je dus maar 1 keer inlezen.. gewoon bij het opstarten van het systeem alle statemachine-beschrijvingen aanmaken (een statemachine beschrijving is een statemachine waarvoor je nog geen instantie hebt aangemaakt).
Ik dacht dat ik dit ook voorbij zag komen laatst op theserverside. Was dit niet voor de J2se 1.6? *is vandaag ook lui* ;)
Het zou al in 1.5 komen. Het is er niet van gekomen, dus of het komt in een update of in 1.6. Er is al wel een opensource alternatief die je vandaag kunt gebruiken.

[ Voor 9% gewijzigd door Alarmnummer op 28-03-2005 16:51 ]


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

flowerp schreef op maandag 28 maart 2005 @ 16:45:
Ik maak een statemachine loader die de concrete classes in laadt. Via een synchronized static map maakt deze een mapping naar een daarwerkelijke classe naam. Als er nu een nieuwe versie van een gecompileerd XML bestand komt (een nieuwe classe naam dus), dan maak ik via een admin interface een nieuwe mapping aan. Nieuwe clients krijgen dan automatisch de nieuwe class. Het is nog maar een idee'tje en moet er eigenlijk nog een beetje beter over nadenken :)
KISS.. hoe vaak pas je nou die mapping aan? Voor jsp pagina`s kan ik het me voorstellen dat een developer niet helemaal wel redeployen. Maar als je exploded dirs gebruikt (ik weet niet of iedere app server dat aankan trouwens) gaat het heel rap. Ik zou niet zo ingewikkeld proberen te doen maar de essentie van het probleem oplossen (xml eenmalig inparsen) en voor een eenvoudige oplossing kiezen (redeploy).

  • marcusk
  • Registratie: Februari 2001
  • Laatst online: 26-09-2023
Misschien te voor de hand liggend, maar waarom gebruik je niet gewoon Serialization om je objecten te laden? Dynamische code generatie lijkt mij nogal overkill in dit geval, en ik zie niet in welke voordelen het hier biedt.
flowerp schreef op maandag 28 maart 2005 @ 16:45:
De echte techneut zal er van gruwelen, maar een manager van een project waar ik ooit opzat wilde echt dat we alle logica in JSPs zetten omdat "de server herstarten gewoon niet kan, en er altijd aanpassingen gemaakt moeten worden".
Je hoeft toch alleen de betreffende webapp te restarten? En als ook dat een probleem is zijn er wel andere oplossingen te bedenken. Als ik alle logica in de JSPs zou moeten stoppen gebruik ik nog liever PHP :)

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

marcusk schreef op maandag 28 maart 2005 @ 18:12:
Misschien te voor de hand liggend, maar waarom gebruik je niet gewoon Serialization om je objecten te laden? Dynamische code generatie lijkt mij nogal overkill in dit geval, en ik zie niet in welke voordelen het hier biedt.
De objecten zullen wel ergens aangemaakt moet worden zodat ze geserialized kunnen worden.

  • marcusk
  • Registratie: Februari 2001
  • Laatst online: 26-09-2023
Alarmnummer schreef op maandag 28 maart 2005 @ 18:17:
De objecten zullen wel ergens aangemaakt moet worden zodat ze geserialized kunnen worden.
Dat begrijp ik. Maar hoe is dit anders van het codegen geval?

"In plaats van on-the-fly te parsen en mijn interne structuur op te bouwen kan ik ook eenmalig van te voren een class genereren vanuit mijn XML file."

Dat wordt dus eenmalig de class uit de XML laden, vervolgens dmv Serialization.

Hoe dan ook, ik kan echt geen reden bedenken waarom je hierbij code generatie zou gebruiken.

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

marcusk schreef op maandag 28 maart 2005 @ 18:27:
[...]
Dat begrijp ik. Maar hoe is dit anders van het codegen geval?
Daar zet je de objecten in elkaar (en bedenk je tevens de juiste classes). In jouw geval doe je niets anders dan reeds bestaande objecten weer terug in het geheugenkrijgen.
Hoe dan ook, ik kan echt geen reden bedenken waarom je hierbij code generatie zou gebruiken.
State machine`s kun je heel mooi vertalen naar code. Check bv SableCC maar eens,

  • marcusk
  • Registratie: Februari 2001
  • Laatst online: 26-09-2023
Alarmnummer schreef op maandag 28 maart 2005 @ 18:38:
State machine`s kun je heel mooi vertalen naar code. Check bv SableCC maar eens.
Ok, daar heb je gelijk in, niet aan gedacht :)

  • flowerp
  • Registratie: September 2003
  • Laatst online: 04-02 02:01
Alarmnummer schreef op maandag 28 maart 2005 @ 16:50:
[...]

Uhh.. je kunt toch zelf wel nagaan dat je een performance winst kan halen door het inlezen eenmalig te latengebeuren?
Ja natuurlijk :) Maar de afweging die ik moet maken is of het de develop tijd waard is om voor iets te gaan optimaliseren wat mischien niet zo'n grote overhead is en waarbij de complexiteit van de code ook nog eens toeneemt.
Dus 1 keer per statemachine en verder niet weer. Alle gebruikers delen 1 instantie van een statemachine beschrijving..
Het is helaas met het huidige data model niet zo makkelijk. Er komen in de beschrijving nogal een aantal hashmaps voor. Deze zijn bijvoorbeeld nodig om per state een aantal properties op te geven. Aan de hand van de daadwerkelijke data (waar de states betrekking op hebben), worden voor logische data namen properties opgevraagd. In de meest recht toe recht aan versie ontkom je dus niet aan (hash)maps.

Structuren waarin deze voorkomen kun je niet makkelijk sharen. Ik kan de map zelf synchronized maken, maar dan kan je er nog steeds niet efficient shared over ittereren en betaal je telkens de overhead van synchronisatie.

Bij code generatie zou ik wellicht een aantal gevallen door gegenereerde switch constructies kunnen vervangen, maar met het huidige model is sharen dus niet echt makkelijk.

Een andere mogelijkheid waar ik nog aan zat te denken is een enkele globale loader (die de parsing doet als er nog niet geladen is), en die per request een clone geeft van de state machine en de properties per state.
Het zou al in 1.5 komen. Het is er niet van gekomen, dus of het komt in een update of in 1.6. Er is al wel een opensource alternatief die je vandaag kunt gebruiken.
Als ik voor de code generatie oplossing ga, zou ik wellicht voor een standalone 'compiler' gaan die classes uitspuugt. Deze classes zet ik dan gewoon in een directory en laad ze dynamisch met Class.forName() in mijn applicatie. Het genereren van mijn .java is dan gewoon tekst manipulatie en javac run ik dan desnoods met de hand hierover heen.

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


  • flowerp
  • Registratie: September 2003
  • Laatst online: 04-02 02:01
marcusk schreef op maandag 28 maart 2005 @ 18:12:
Misschien te voor de hand liggend, maar waarom gebruik je niet gewoon Serialization om je objecten te laden?
Serialization opzich is een te fragiel mechanisme om cross vm en cross platform te gebruiken.
Dwz, ik kan natuurlijk niet via serialization van te voren files maken die ik mee deploy.
Ik zou het wel kunnen (mis)gebruiken om een in-memory deep-copy te maken. De state machine loader parsed dan de xml, en gebruikt dan serialization om heel makkelijk een totale clone van de gehele structuur te maken. Deze techniek heb ik vroeger wel eens vaker in projecten gebruikt.
Je hoeft toch alleen de betreffende webapp te restarten?
De webserver draait zowieso maar 1 webapp, maar ook zonder dat geloof ik niet dat de meeste webservers een aparte webapp kunnen restarten. Hoe dan ook, van de manager in kwestie mocht de server dus absoluut niet herstart worden, maar wilde ie wel tussen releases in veranderingen doen. Vanwege deze voorkeur kwam er dus erg veel code direct in JSPs terecht voor dat project.

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


Verwijderd

Je kunt ook gewoon je geparste structuur per machine handler cachen?

Als je het over gemiddeld 20 states hebt en wat properties, dan zal dat nooit super groot zijn. Zelfs met 10 xml documenten en 10 concurrent users, en *heel erg ruim* neem ik even 1/2k per state, dan zit je nog maar op 20*10*10 * 1/2k = 1MB totaal. Zelfs met 100 users (lijkt me erg veel, maar toch) zit je nog maar op 10MB.

Als je bedenkt dat servers tegenwoordig minimaal 1GB aan RAM hebben, dan valt dit dus totaal in het niet. Tenzij jou states echt VEEL data bevatten dan is simpele caching per user het makkelijkst.

Verwijderd

Verwijderd schreef op dinsdag 29 maart 2005 @ 11:12:
Je kunt ook gewoon je geparste structuur per machine handler cachen?

Als je het over gemiddeld 20 states hebt en wat properties, dan zal dat nooit super groot zijn. Zelfs met 10 xml documenten en 10 concurrent users, en *heel erg ruim* neem ik even 1/2k per state,
Zo ruim is dat nou ook weer niet hoor. Ik zit met een vergelijkbaar project, waar in XML een filter tree gespecificeerd wordt (eigenlijk een DAG). Als ik het hele document omzet naar mijn eigen datastructuur, dan zit ik op zo'n 1.5k per filter. Een filter bevat een aantal parameters, en een aantal koppelingen naar volgende filters (als het een zuivere tree was had ik deze kopelingen overigens weg kunnen laten en de natuurlijke hierarchy van XML kunnnen benutten).

De structuur voor de filter beschrijving bevat 10 arraylists, waar verschillende dingen in staan zoals namen van de input streams, commando's om data van externe databronnen op te halen, en namen van gekoppelde filters.

Met een profiler heb ik eens gekeken waar de overhead kwa ruimte in ging zitten. Het viel toen op (had ik al kunnen weten) dat lege ArrayLists altijd 10 plaatsen alloceren. Gelukkig hebben deze objecten een trim functie, maar zelfs dan nemen ze leeg nog steeds 36bytes (volgens profiler) aan geheugen in.

Een typisch XML filter document is bij mij ongeveer 24k op de harde schijf, terwijl de interne structuur zo'n 70k is (46 filters). Met gebruik van de standaard Java SAX parser zie ik parse tijden van +- 300 t/m 650ms. Voor parsen maak ik telkens een nieuwe parser aan, maar toch zie ik dat parse tijden na enkele keren parsen inderdaad afnemen.

Zelf zat ik ook te kijken naar code generation. Het scheelt de parse overhead, en in sommige gevallen geheugen. Statements kunnen altijd geshared worden. Je moet dan wel bedenken dat je sommige dingen heel anders moet aanpakken. Zo gebruik ik nu een ArrayList met namen van volgende filters en dan een HashMap om deze 'volgende' filters op te zoeken. Met code generatie zou je dit kunnen vervangen door je filters (of states) te nummeren en je HashMap lookup te vervangen door een grote switch waarbij bij elke case meteen de gegenereerde code staat die alles doet (je beschrijving en machine worden dan 1 geheel).

Heb je een design wat echt perse een universele machine nodig heeft die met losse states werkt (die opgevraagd worden vanuit de beschrijving structuur), dan kun je met code generatie en de switch de state ter plekke genereren bij opvragen.

Bijvoorbeeld:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
State getState(int nr) {

   switch ( nr ) {
      case 0:
         State state = new State();
         state.nr = 0;
         state.param1 = "foo";
         state.param2 = "bar";
         state.next.add(1);
         state.next.add(2);
         return state;
     case 1:
         State state = new State();
         state.nr = 1;
         state.param1 = "bar";
         state.param2 = "foo";
         state.next.add(2);;
         return state;
     }

}


In bovenstaande situatie win je parse time als normaal gebruik niet alle states nodig heeft en ze maar 1 maal doorlopen worden, maar als je states of filters (of whatever) meer dan eenmaal doorlopen worden verlies je weer, omdat het opvragen van een state nu langer duurt. Alternatief kun je ook je states bij het instantieeren van het object in een primitive array plaatsen en dan de initializer syntax gebruiken. Dit is wel echt een micro optimalisatie kwa snelheid. Als je een realtime applicatie hebt die een filter-tree voor gegenereerde beeldjes oproept met 100fps dan kan het nuttig zijn. Maakt je machine een state transition op verzoek van de user (laten we zeggen een transition per 2 seconden), dan is het absolute onzin om daar voor te gaan optimizen op speed.

Maar het gangbaarste bij code generatie is toch om de machine zelf mee te genereren en dan voor bepaalde situaties callback code te installeren. Daar win je het meeste mee.
Als je een bestaand design hebt wat met een universele machine werkt en losse beschrijvingen (waarbij de bron bv XML is), dan moet je toch je design behoorlijk omgooien om nog winst uit code generatie te halen.

Wat overigens nog wel een echt voordeel is bij code generatie van een XML file, is dat je veel makkelijker kunt controleren of alles klopt in je document. Bij het realtime parsen doe je waarschijnlijk alleen de meest broodnodige checks (als je die al doet), maar bij pre-compilatie kun je een hele reeks aan analyzes erover heen laten gaan die best wel een paar seconden mogen duren.

Verwijderd

flowerp schreef op maandag 28 maart 2005 @ 20:21:
Het is helaas met het huidige data model niet zo makkelijk. Er komen in de beschrijving nogal een aantal hashmaps voor.
[...]
Structuren waarin deze voorkomen kun je niet makkelijk sharen.
Wat je mischien nog kunt doen is een splitsing maken tussen je data en je data access. Als je nu al je data in een stuctuur zet waar alleen primitives in voorkomen: ints, strings, primitive arrays, etc.

Deze zijn per definitie thread safe.

Dit raw-data-object geef je door aan een class die het decorate met access methods. Vooral voor iterators over arrays is dit makkelijk. Je data decorator legt bijvoorbeeld een ArrayList accesor over je ruwe array heen.

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Volgens mij zoek je iets als gsoap voor Java?

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein

Pagina: 1