Showing posts with label Tipp. Show all posts
Showing posts with label Tipp. Show all posts

Tuesday, April 24, 2012

"Recycle" ein Versuch einer Erklärung 2. Teil

Im Teil 1 haben wir geklärt, warum man Notes Objekte überhaupt recyceln muß und wie man die Session am Ende eines Programms ordentlich beendet. Bei ganz kleinen Programmen würde das auch toll funktionieren, aber sobald Programme mehr Daten verarbeiten und seien Sie sich sicher, auch wenn die Datenmenge derzeit noch überschaubar ist, irgendwann wird sie so groß, dass die verfügbaren handles oder der Speicherplatz ausgehen.

Deshalb gilt folgender Merksatz: Jedes Notesobjekt soll so rasch als möglich wieder recycelt werden. Spätestens jedoch bevor es derefenziert wird.

Ein Beispiel:
if(flag) 
 Document doc=view.getFirstDocument(); 
 System.out.println(doc.getItemValueString("Test"); 
} 

Die Variable doc ist nur innerhalb des ifs gültig und muss deshalb noch innerhalb des if's recycelt werden.

if(flag){
  Document doc=view.getFirstDocument();
  System.out.println(doc.getItemValueString("Test");
  doc.recycle();
}

Es gibt beim Recycle jedoch eine Erleichterung, die einem einige Arbeit spart. Das recyceln von übergeordneten Objekten recycelt alle abgeleiteten Objekte mit.

Auf unser Beispiel übertragen:

View view=cdb.getView("Test");
if(flag){
  Document doc=view.getFirstDocument();
  System.out.println(doc.getItemValueString("Test");
  // doc.recycle(); kann man sich sparen, da das Dokument beim Recyclen der View mitrecycelt wird

}
else{
  Document doc=view.getLastDocument();
  System.out.println(doc.getItemValueString("Test");
   // doc.recycle(); kann man sich sparen, da das Dokument beim Recyclen der View mitrecycelt wird
}
view.recycle();

Man sollte dieses implizite Recycle aber nur dann verwenden, wenn man wie in unseren Beispiel gewährleisten kann, dass nur sehr wenige Objekte erzeugt werden und diese auch nicht mit der Datenmenge mehr werden.

Fatal wäre folgender Code:
View view=cdb.getView("Test");
Document doc=view.getFirstDocument();
while(doc!=null){
  //Mach was mit doc
  doc=view.getNextDocument();
}
view.recycle();

Dieser Code wird spätestens, bei ein paar Hundert Dokumenten in der View crashen.

Man muß also doc bei jedem Schleifendurchlauf recyceln. Das ist jedoch nicht ganz einfach, da doc ja nach getNextDocument nicht mehr da ist und vorher darf ich es nicht recyclen, da sonst getNextDocument() nicht mehr funktioniert, wenn das aktuelle Dokument recycelt wird. Folgendes Pattern hat sich bewährt:

View view=cdb.getView("Test");
Document doc=view.getFirstDocument();
while(doc!=null){
  //Mach was mit doc
  Document tempdoc=doc;
  doc=view.getNextDocument();
  tempdoc.recycle();
}
view.recycle();

 Aufpassen muß man natürlich auch darauf, dass man nicht Objekte implizit bereinigt die man noch gerne verwenden möchte:

View view=cdb.getView("Test");
Document doc=view.getFirstDocument();
view.recycle(); //Hier wird doc implizit mitrecycelt. Man darf doc nicht mehr verwenden.
System.out.println(doc.getItemValueString("form")); //Hier wird das Programm crashen.

Ich hoffe etwas Licht in die Sache gebracht zu haben. Würde mich auch über Kommentare zu spezifischen Problemen freuen, die ich dann im 3. Teil behandeln werde.

Thursday, April 19, 2012

Stacküberlauf beim Debuggen von Notesplugins

Wenn man Lotus Notes aus Eclipse zum Debuggen von selbsterstellten Plugins aufruft, wird das UI von Lotus Notes nicht richtig angezeigt und man bekommt einen sehr langen Stacktrace der mit

at java.util.regex.Pattern$Curly.match1(Pattern.java:3808) 
at java.util.regex.Pattern$Curly.match(Pattern.java:3757)
at java.util.regex.Pattern$GroupHead.match(Pattern.java:4179)
at java.util.regex.Pattern$GroupHead.match(Pattern.java:4179)
at java.util.regex.Pattern$Curly.match0(Pattern.java:3800)
at java.util.regex.Pattern$Curly.match(Pattern.java:3755)
at java.util.regex.Pattern$Begin.match(Pattern.java:3131)
at java.util.regex.Matcher.search(Matcher.java:1116)
at java.util.regex.Matcher.find(Matcher.java:546)
at com.ibm.css.parser.CSSParserImpl.parse(CSSParserImpl.java:106)
at com.ibm.css.parser.CSSParserImpl.parse(CSSParserImpl.java:59)
at com.ibm.rcp.ui.css.StyleManager.handleRules(StyleManager.java:269)
at com.ibm.rcp.ui.css.StyleManager.handleRules(StyleManager.java:275)
at com.ibm.rcp.ui.css.StyleManager.parse(StyleManager.java:218)
at com.ibm.rcp.ui.internal.themes.ThemeManager.setCurrentTheme(ThemeManager.java:303)
at com.ibm.rcp.ui.internal.themes.ThemeManager.<init>(ThemeManager.java:187)
at com.ibm.rcp.ui.internal.themes.ThemeManager.getInstance(ThemeManager.java:216)
at com.ibm.rcp.ui.internal.themes.ThemeServiceFactory.getService(ThemeServiceFactory.java:30)
at org.eclipse.osgi.framework.internal.core.ServiceUse$1.run(ServiceUse.java:117)
at java.security.AccessController.doPrivileged(AccessController.java:202)
at org.eclipse.osgi.framework.internal.core.ServiceUse.getService(ServiceUse.java:115)

 .. 51 more

endet.


Der Grund dafür ist, dass im DebugMode der Speicher auf dem Stack ausgeht. Dieses Problem kann man einfach umgehen in dem man in der Launch Configuration von Lotus Notes in Eclipse den JVM Parameter Xss1m setzt.

Folgende Schritte sind für die Änderung durchzuführen:

Den Debug Configurations Dialog über das Menü aufrufen


In der Launchconfiguration von Lotus Notes auf dem zweiten Tab "Arguments" den zusätzlichen Paramter Xss1m setzen.

Dann auf Apply drücken und die Debug Configuration starten. Dann sollte Notes ohne UI Probleme auch im Debug Mode laufen.

Sunday, April 15, 2012

Peformance Trick beim Durchlesen von Views


Eines vorweg für alle die mit Java auf Kriegsfuß stehen. Der Beispielcode ist zwar in Java. Das selbe sollte jedoch auch in anderen Sprachen z.B. Lotusscript funktionieren.  Ich zeige dann am Ende des Posts den Code auch in Lotusscript versprochen

In vielen meiner Programme oder Agenten kommt Code vor, der alle Dokumente einer bestimmten View lesen und verarbeiten muss.  wie z.B.


...
Document doc = view.getFirstDocument();
while (doc != null) {
     Document tempdoc = doc;
    
//Mach irgendwas mit diesem doc

     doc = view.getNextDocument(doc);
     tempdoc.recycle();
}

...

Ausführungsdauer  dieses Code bei unserer CRM Datenbank ca. 43 Sekunden.

Dabei war ich bis jetzt immer sehr enttäuscht über die Performance die Notes hier bietet. Das lesen von größeren Datenmengen dauert einfach ewig. Was hauptsächlich daran liegt, dass jeder Lesevorgang in der View eine Transaktion auslöst. Viel effizienter wäre es wenn Notes gleich ganze Blöcke lesen würde. Bisher gab es dazu keine Möglichkeit im Domino API.  Bei der view gibt es das auch nach wie vor nicht. Aber im Rahmen der Performanceverbesserungen bei xPages wurden neue Features beim ViewNavigator eingeführt und die kann man sich hier zunutze machen.

Hier der selbe Code wie oben nur umgebaut auf die Verwendung eines ViewNavigators:
...
ViewNavigator navigator = view.createViewNav();
ViewEntry entry = navigator.getFirstDocument();
while (entry != null) {
   Document doc = entry.getDocument();
 
//Mach irgendwas mit diesem doc
   ViewEntry tempEntry = entry;
   entry = navigator.getNextDocument();
   tempEntry.recycle();
}

...
Das bringt natürlich noch keine Verbesserung des Laufzeitverhalten, sondern bringt sogar eine Verschlechterung auf 53 Sekunden.

Aber der ViewNavigator besitzt neue Methoden die ein geblocktes Lesen erlauben. Die Voraussetzung dafür ist einmal, dass man die View aus dem man den Navigator erstellt das Autoupdate abgewöhnt mit view.setAutoUpdate(false).

Dann kann man den ViewNavigator mit navigator.setBufferMaxEntries(x) mitteilen, dass man gerne x Sätze auf einmal lesen will. Dies verbessert die Performance enorm. (Update laut Domino Wiki darf der Wert x zwischen (2 und 400) sein. Ich habe festgestellt, dass höhere Werte als 400 eine leicht bessere performance bringen. Siehe auch Kommentag von Ulrich Krause)

Folgender Code von oben ergänzt mit Blocking läuft auf meiner Maschine in unter 10 Sekunden statt der 43 Sekunden im usprünglichen Code ohne Blocking. 

...
view.setAutoUpdate(false);
ViewNavigator navigator = view.createViewNav();
navigator.setBufferMaxEntries(1024);
ViewEntry entry = navigator.getFirstDocument();
while (entry != null) {
   Document doc = entry.getDocument();
 
//Mach irgendwas mit diesem doc
   ViewEntry tempEntry = entry;
   entry = navigator.getNextDocument();
   tempEntry.recycle();
}

...
Das ist ja schon mal nicht schlecht.

Es gibt aber noch 2 zusätzliche Optionen mit denen man unter bestimmten Umständen noch zusätzliche Performance erzielen kann.

Wenn man wie in den obigen Beispiel die viewEntrys Spalten nicht benötigt, dann kann man das laden der Spaltenwerte unterdrücken. Je nach dem wie viele Spalten die View enthält, habe ich in meinem Tests nocheinmal eine Laufzeitreduktion um 10% erreichen können. Diese Option kann man mit navigator.setEntryOptions(ViewNavigator.VN_ENTRYOPT_NOCOLUMNVALUES) setzen.

Bei manchen Arten von Views kann es auch etwas bringen, die Entryoption navigator.setEntryOptions(ViewNavigator.VN_ENTRYOPT_NOCOUNTDATA); zu setzen. Laut meinen Informationen verhindert es die automatische Zählung bei kategorisierten Ansichten. Ich habe es aber noch nicht praktisch verwendet.

Noch einen zusätzlichen Performancegewinn, kann man erzielen, wenn man nur bestimmte Dokumente aus der View verarbeiten will, in dem man in der View die Selektionsfelder als Spalten aufnimmt, und dann das Dokument nur dann liest, wenn im Viewentry die Spaltenwerte den Selektionskriterien entsprechen. Hier sind die Performancegewinne noch stärker ausgeprägt als bei der klassischen Methode. Außerdem kann man natürlich noch die ganzen anderen Vorteile eines ViewNavigators verwenden. z.B. einen ViewNavigator mit allen Dokumenten einer bestimmten Kategorie einer kategorisierten View zu erstellen.

Hier noch wie am Anfang versprochen das oben angeführte Beispiel in Lotus script:

...
view.Autoupdate=false
Set navigator=view.Createviewnav()
navigator.Buffermaxentries=1024
navigator.Entryoptions=1  ' keine Spaltenwerte lesen.
Set entry=navigator.Getfirst()
While Not entry Is Nothing
    Set doc=entry.Document
    'Mach was mit dem Dokument
    Set entry=navigator.Getnext(entry)
Wend

... 

Würde mich über Kommentare zu den Performanceverbesserungen die Ihr in euren Anwendungen erzielen konntet freuen.

ad