From 9267f7f233deff88f3fd511bfc3f8e4c6aaa5151 Mon Sep 17 00:00:00 2001 From: Radek Oslejsek <oslejsek@fi.muni.cz> Date: Tue, 15 Feb 2022 15:19:57 +0100 Subject: [PATCH] Using Kryo with objenesis to accelerate serialization --- Comparison/pom.xml | 12 ++- .../cz/fidentis/analyst/face/HumanFace.java | 65 ++----------- .../analyst/face/HumanFaceFactory.java | 93 ++++++++++++++++++- .../batch/ApproxHausdorffDistTask.java | 2 +- .../cz/fidentis/analyst/batch/BatchPanel.form | 27 ++++-- .../cz/fidentis/analyst/batch/BatchPanel.java | 32 +++++-- .../fidentis/analyst/batch/Bundle.properties | 1 + 7 files changed, 155 insertions(+), 77 deletions(-) diff --git a/Comparison/pom.xml b/Comparison/pom.xml index 6480685e..3135753c 100644 --- a/Comparison/pom.xml +++ b/Comparison/pom.xml @@ -85,9 +85,19 @@ <dependency> <groupId>de.ruedigermoeller</groupId> <artifactId>fst</artifactId> - <version>2.57</version> + <version>3.0.3</version> </dependency> --> + <dependency> + <groupId>com.esotericsoftware</groupId> + <artifactId>kryo</artifactId> + <version>5.3.0</version> + </dependency> + <dependency> + <groupId>org.objenesis</groupId> + <artifactId>objenesis</artifactId> + <version>3.2</version> + </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> diff --git a/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java b/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java index 12707870..52e5700c 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java @@ -14,16 +14,10 @@ import cz.fidentis.analyst.visitors.face.HumanFaceVisitor; import cz.fidentis.analyst.visitors.mesh.BoundingBox; import cz.fidentis.analyst.visitors.mesh.BoundingBox.BBox; import java.awt.image.BufferedImage; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.RandomAccessFile; import java.io.Serializable; import java.nio.file.Path; import java.util.Collections; @@ -66,7 +60,7 @@ public class HumanFace implements Serializable { private final String id; - private BufferedImage preview; + private transient BufferedImage preview; /** * Fast (de)serialization handler @@ -140,6 +134,14 @@ public class HumanFace implements Serializable { eventBus = new EventBus(); } + /** + * Non-public constructor necessary for the Kryo de-serialization + */ + private HumanFace() { + id = null; + eventBus = new EventBus(); + } + /** * Returns the triangular mesh model of the human face. * @@ -407,55 +409,6 @@ public class HumanFace implements Serializable { return null; } - /** - * Creates serialized dump of the human face. Event buses are not stored. - * Therefore, listeners have to re-register again after recovery. - * - * @return Dump file - * @throws IOException on error in creating the dump file - */ - public File dumpToFile() throws IOException { - File tempFile = File.createTempFile(this.getClass().getSimpleName(), ".ser"); - tempFile.deleteOnExit(); - RandomAccessFile raf = new RandomAccessFile(tempFile, "rw"); - - try (ObjectOutputStream fos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(raf.getFD())))) { - fos.writeObject(this); - fos.flush(); - } - - /* - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tempFile))) { - FSTObjectOutput out = FST_CONF.getObjectOutput(bos); - out.writeObject(this, HumanFace.class); - out.flush(); - } - */ - - return tempFile; - } - - /** - * Restores human face from a dump file. - * - * @param dumpFile The file - * @return Human face - * @throws IOException on error in reading the dump file - * @throws java.lang.ClassNotFoundException on error when instantiating the human face - */ - public static HumanFace restoreFromFile(File dumpFile) throws IOException, ClassNotFoundException { - try (ObjectInputStream fos = new ObjectInputStream(new BufferedInputStream(new FileInputStream(dumpFile)))) { - return (HumanFace) fos.readObject(); - } - /* - try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(dumpFile))) { - FSTObjectInput in = FST_CONF.getObjectInput(bis); - HumanFace face = (HumanFace) in.readObject(HumanFace.class); - return face; - } - */ - } - /** * Visits this face. * diff --git a/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFaceFactory.java b/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFaceFactory.java index 176fd5da..85e3b9d8 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFaceFactory.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFaceFactory.java @@ -1,12 +1,32 @@ package cz.fidentis.analyst.face; +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; +import com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy; +import cz.fidentis.analyst.Logger; +import cz.fidentis.analyst.kdtree.KdNode; +import cz.fidentis.analyst.kdtree.KdTree; +import cz.fidentis.analyst.mesh.core.CornerTable; +import cz.fidentis.analyst.mesh.core.CornerTableRow; +import cz.fidentis.analyst.mesh.core.MeshFacetImpl; +import cz.fidentis.analyst.mesh.core.MeshModel; +import cz.fidentis.analyst.mesh.core.MeshPointImpl; +import cz.fidentis.analyst.symmetry.Plane; +import cz.fidentis.analyst.visitors.mesh.BoundingBox.BBox; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import java.util.logging.Level; +import javax.vecmath.Point3d; +import javax.vecmath.Vector3d; +import org.objenesis.strategy.StdInstantiatorStrategy; /** * A flyweight factory that creates and caches human faces. Faces are @@ -19,6 +39,8 @@ import java.util.logging.Level; * @author Radek Oslejsek */ public class HumanFaceFactory { + + private static Kryo kryo; /** * Memory clean up, i.e. dumping the file into disk and de-referencing it, is @@ -82,7 +104,32 @@ public class HumanFaceFactory { private Strategy strategy = Strategy.LRU; private boolean reuseDumpFile = false; - + + /** + * Constructor. + */ + public HumanFaceFactory() { + synchronized (this) { + if (HumanFaceFactory.kryo == null) { + HumanFaceFactory.kryo = new Kryo(); + HumanFaceFactory.kryo.setInstantiatorStrategy(new DefaultInstantiatorStrategy(new StdInstantiatorStrategy())); + HumanFaceFactory.kryo.register(HumanFace.class); + HumanFaceFactory.kryo.register(MeshModel.class); + HumanFaceFactory.kryo.register(MeshFacetImpl.class); + HumanFaceFactory.kryo.register(MeshPointImpl.class); + HumanFaceFactory.kryo.register(CornerTable.class); + HumanFaceFactory.kryo.register(CornerTableRow.class); + HumanFaceFactory.kryo.register(BBox.class); + HumanFaceFactory.kryo.register(Plane.class); + HumanFaceFactory.kryo.register(KdTree.class); + HumanFaceFactory.kryo.register(KdNode.class); + HumanFaceFactory.kryo.register(Point3d.class); + HumanFaceFactory.kryo.register(Vector3d.class); + HumanFaceFactory.kryo.register(ArrayList.class); + HumanFaceFactory.kryo.register(HashMap.class); + } + } + } /** * Changes the dumping strategy @@ -176,11 +223,12 @@ public class HumanFaceFactory { // Free memory and recover human face from dump file: HumanFace face; try { - face = HumanFace.restoreFromFile(dumpFile); // recover face from disk + face = restoreFromFile(dumpFile); // recover face from disk checkMemAndDump(); // free the memory, if necessary //System.out.println(face.getShortName() + " recovered"); - } catch (ClassNotFoundException|IOException ex) { - java.util.logging.Logger.getLogger(HumanFaceFactory.class.getName()).log(Level.SEVERE, null, ex); + } catch (IOException ex) { + Logger.print("HumanFaceFactory ERROR: " + ex); + //java.util.logging.Logger.getLogger(HumanFaceFactory.class.getName()).log(Level.SEVERE, null, ex); return null; } @@ -280,7 +328,7 @@ public class HumanFaceFactory { HumanFace faceToDump = this.inMemoryFaces.remove(time); this.usage.remove(faceToDump.getId()); if (!reuseDumpFile || !dumpedFaces.containsKey(faceToDump.getId())) { // dump only if it's required - dumpedFaces.put(faceToDump.getId(), faceToDump.dumpToFile()); + dumpedFaces.put(faceToDump.getId(), dumpToFile(faceToDump)); } //System.out.println(faceToDump.getShortName() + " dumped"); } @@ -304,4 +352,39 @@ public class HumanFaceFactory { long allocatedMemory = (Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()); return Runtime.getRuntime().maxMemory() - allocatedMemory; } + + protected File dumpToFile(HumanFace face) throws IOException { + File tempFile = File.createTempFile(this.getClass().getSimpleName(), ".bin"); + tempFile.deleteOnExit(); + + /* + RandomAccessFile raf = new RandomAccessFile(tempFile, "rw"); + try (ObjectOutputStream fos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(raf.getFD())))) { + fos.writeObject(this); + fos.flush(); + } + */ + + try (Output out = new Output(new FileOutputStream(tempFile))) { + kryo.writeObject(out, face); + } catch(Exception ex) { + throw new IOException(ex); + } + + return tempFile; + } + + protected static HumanFace restoreFromFile(File dumpFile) throws IOException { + /* + try (ObjectInputStream fos = new ObjectInputStream(new BufferedInputStream(new FileInputStream(dumpFile)))) { + return (HumanFace) fos.readObject(); + } + */ + + try (Input in = new Input(new FileInputStream(dumpFile))) { + return kryo.readObject(in, HumanFace.class); + } catch(Exception ex) { + throw new IOException(ex); + } + } } diff --git a/GUI/src/main/java/cz/fidentis/analyst/batch/ApproxHausdorffDistTask.java b/GUI/src/main/java/cz/fidentis/analyst/batch/ApproxHausdorffDistTask.java index f7666fbf..8cbb5965 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/batch/ApproxHausdorffDistTask.java +++ b/GUI/src/main/java/cz/fidentis/analyst/batch/ApproxHausdorffDistTask.java @@ -96,7 +96,7 @@ public class ApproxHausdorffDistTask extends SimilarityTask { HausdorffDistance.Strategy.POINT_TO_POINT, true, // relative distance true, // parallel - true // crop + getControlPanel().getHdAutoCrop() ); templateFace.getMeshModel().compute(hd); distCache.add(hd.getDistances().values().stream() // Store relative distances of vertices to the cache diff --git a/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.form b/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.form index 44cfd207..aa6f9a1a 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.form +++ b/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.form @@ -279,18 +279,20 @@ <Group type="102" attributes="0"> <EmptySpace max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="0" attributes="0"> - <Component id="jComboBox2" min="-2" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> - <Component id="jButtonInfo1" min="-2" max="-2" attributes="0"/> - </Group> <Group type="102" alignment="0" attributes="0"> <Component id="jButton4" min="-2" max="-2" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/> <Component id="jButton6" min="-2" max="-2" attributes="0"/> </Group> + <Group type="102" alignment="0" attributes="0"> + <Component id="jComboBox2" min="-2" max="-2" attributes="0"/> + <EmptySpace type="unrelated" max="-2" attributes="0"/> + <Component id="jCheckBox5" min="-2" max="-2" attributes="0"/> + <EmptySpace type="unrelated" max="-2" attributes="0"/> + <Component id="jButtonInfo1" min="-2" max="-2" attributes="0"/> + </Group> </Group> - <EmptySpace pref="359" max="32767" attributes="0"/> + <EmptySpace max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> @@ -300,7 +302,10 @@ <EmptySpace min="-2" pref="14" max="-2" attributes="0"/> <Group type="103" groupAlignment="0" max="-2" attributes="0"> <Component id="jButtonInfo1" max="32767" attributes="0"/> - <Component id="jComboBox2" max="32767" attributes="0"/> + <Group type="103" groupAlignment="3" attributes="0"> + <Component id="jComboBox2" alignment="3" max="32767" attributes="0"/> + <Component id="jCheckBox5" alignment="3" min="-2" max="-2" attributes="0"/> + </Group> </Group> <EmptySpace type="unrelated" max="-2" attributes="0"/> <Group type="103" groupAlignment="3" attributes="0"> @@ -358,6 +363,14 @@ <Property name="enabled" type="boolean" value="false"/> </Properties> </Component> + <Component class="javax.swing.JCheckBox" name="jCheckBox5"> + <Properties> + <Property name="selected" type="boolean" value="true"/> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="cz/fidentis/analyst/batch/Bundle.properties" key="BatchPanel.jCheckBox5.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + </Component> </SubComponents> </Container> </SubComponents> diff --git a/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.java b/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.java index 955780a0..49ae71c1 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.java +++ b/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.java @@ -124,6 +124,10 @@ public class BatchPanel extends ControlPanel { return getStaticIcon(); } + public boolean getHdAutoCrop() { + return jCheckBox5.isSelected(); + } + /** * Sets the similarity export button * @@ -275,6 +279,11 @@ public class BatchPanel extends ControlPanel { + "<br/>" + "<strong>" + SIMILARITY_COMPLETE_HD + "</strong>: <br/>" + "Hausdorff distance is computed for all paris in both directions.<br/>" + + "<br/>" + + "<strong>HD auto-crop</strong>: <br/>" + + "If selected, then only overlapping parts of faces are included<br/>" + + "in the calculation of Hausdorff distance.<br/>" + + "<br/>" + "</html>", "Distance computation strategies", JOptionPane.INFORMATION_MESSAGE @@ -330,6 +339,7 @@ public class BatchPanel extends ControlPanel { jButton4 = new javax.swing.JButton(); jButtonInfo1 = new javax.swing.JButton(); jButton6 = new javax.swing.JButton(); + jCheckBox5 = new javax.swing.JCheckBox(); setPreferredSize(new java.awt.Dimension(600, 600)); @@ -465,6 +475,9 @@ public class BatchPanel extends ControlPanel { org.openide.awt.Mnemonics.setLocalizedText(jButton6, org.openide.util.NbBundle.getMessage(BatchPanel.class, "BatchPanel.jButton6.text")); // NOI18N jButton6.setEnabled(false); + jCheckBox5.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(jCheckBox5, org.openide.util.NbBundle.getMessage(BatchPanel.class, "BatchPanel.jCheckBox5.text")); // NOI18N + javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3); jPanel3.setLayout(jPanel3Layout); jPanel3Layout.setHorizontalGroup( @@ -472,15 +485,17 @@ public class BatchPanel extends ControlPanel { .addGroup(jPanel3Layout.createSequentialGroup() .addContainerGap() .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel3Layout.createSequentialGroup() - .addComponent(jComboBox2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jButtonInfo1)) .addGroup(jPanel3Layout.createSequentialGroup() .addComponent(jButton4) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(jButton6))) - .addContainerGap(359, Short.MAX_VALUE)) + .addComponent(jButton6)) + .addGroup(jPanel3Layout.createSequentialGroup() + .addComponent(jComboBox2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jCheckBox5) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jButtonInfo1))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); jPanel3Layout.setVerticalGroup( jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -488,7 +503,9 @@ public class BatchPanel extends ControlPanel { .addGap(14, 14, 14) .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addComponent(jButtonInfo1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jComboBox2)) + .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jComboBox2) + .addComponent(jCheckBox5))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jButton4) @@ -547,6 +564,7 @@ public class BatchPanel extends ControlPanel { private javax.swing.JCheckBox jCheckBox2; private javax.swing.JCheckBox jCheckBox3; private javax.swing.JCheckBox jCheckBox4; + private javax.swing.JCheckBox jCheckBox5; private javax.swing.JComboBox<String> jComboBox1; private javax.swing.JComboBox<String> jComboBox2; private javax.swing.JLabel jLabel1; diff --git a/GUI/src/main/resources/cz/fidentis/analyst/batch/Bundle.properties b/GUI/src/main/resources/cz/fidentis/analyst/batch/Bundle.properties index abf208f3..514fde3e 100644 --- a/GUI/src/main/resources/cz/fidentis/analyst/batch/Bundle.properties +++ b/GUI/src/main/resources/cz/fidentis/analyst/batch/Bundle.properties @@ -17,3 +17,4 @@ BatchPanel.jLabel2.text=ICP undersampling (100% = none): BatchPanel.jButton1.text=Compute BatchPanel.jButtonInfo1.text= BatchPanel.jButton6.text=Export results +BatchPanel.jCheckBox5.text=HD auto-crop -- GitLab