Skip to content

Commit

Permalink
Merge pull request #56 from torokati44/master
Browse files Browse the repository at this point in the history
Add option to compare by value when memoizing
  • Loading branch information
irmen authored Nov 28, 2017
2 parents df970d6 + c149a78 commit f996dde
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 8 deletions.
32 changes: 24 additions & 8 deletions java/src/main/java/net/razorvine/pickle/Pickler.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ public Memo(Object obj, int index) {
*/
protected boolean useMemo=true;

/**
* When memoizing, compare objects by value. This saves pickle size, but can slow down pickling.
* Alo, it should only be used if the object graph is immutable. Unused if useMemo is false.
*/
protected boolean valueCompare=true;

/**
* The memoization cache.
*/
Expand All @@ -100,7 +106,17 @@ public Pickler() {
* If you use a memo table, you can only pickle objects that are hashable.
*/
public Pickler(boolean useMemo) {
this(useMemo, false);
}

/**
* Create a Pickler. Also specify if it is to compare objects by value.
* If you compare objects by value, the object graph might be altered,
* as different instances with the same value will be unified.
*/
public Pickler(boolean useMemo, boolean valueCompare) {
this.useMemo=useMemo;
this.valueCompare=valueCompare;
}

/**
Expand Down Expand Up @@ -184,15 +200,15 @@ public void save(Object o) throws PickleException, IOException {
* Write the object to the memo table and output a memo write opcode
* Only works for hashable objects
*/
protected void writeMemo( Object obj ) throws IOException
{
protected void writeMemo( Object obj ) throws IOException
{
if(!this.useMemo)
return;
int idHash = System.identityHashCode(obj);
if(!memo.containsKey(idHash))
int hash = valueCompare ? obj.hashCode() : System.identityHashCode(obj);
if(!memo.containsKey(hash))
{
int memo_index = memo.size();
memo.put(idHash, new Memo(obj, memo_index));
memo.put(hash, new Memo(obj, memo_index));
if(memo_index<=0xFF)
{
out.write(Opcodes.BINPUT);
Expand All @@ -213,10 +229,10 @@ protected void writeMemo( Object obj ) throws IOException
private boolean lookupMemo(Class<?> objectType, Object obj) throws IOException {
if(!this.useMemo)
return false;
int idHash = System.identityHashCode(obj);
if(!objectType.isPrimitive()) {
if(memo.containsKey(idHash) && memo.get(idHash).obj == obj) { // same object
int memo_index = memo.get(idHash).index;
int hash = valueCompare ? obj.hashCode() : System.identityHashCode(obj);
if(memo.containsKey(hash) && (valueCompare ? memo.get(hash).obj.equals(obj) : memo.get(hash).obj == obj)) { // same object or value
int memo_index = memo.get(hash).index;
if(memo_index <= 0xff) {
out.write(Opcodes.BINGET);
out.write((byte) memo_index);
Expand Down
54 changes: 54 additions & 0 deletions java/src/test/java/net/razorvine/examples/ValueCompareExample.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package net.razorvine.examples;

import java.util.*;
import java.io.IOException;

import net.razorvine.pickle.Pickler;
import net.razorvine.pickle.Unpickler;

public class ValueCompareExample {

public static void main(String[] args) throws IOException {

Random random = new Random(1337);
List<String> values = new ArrayList<String>();
for (int i = 0; i < 100000; ++i) {
values.add(("This is a string with a number in it: " + random.nextInt(100))
// .intern() // You could also see what happens when the strings are interned
);
}

long t0 = System.nanoTime();
byte[] noValueCompare = new Pickler(true, false).dumps(values);
long t1 = System.nanoTime();
byte[] withValueCompare = new Pickler(true, true).dumps(values);
long t2 = System.nanoTime();

System.out.println("Pickle size without: " + noValueCompare.length + "B, with: " + withValueCompare.length + "B"
+ " (" + (int) ((1 - withValueCompare.length / (double) noValueCompare.length) * 100.0) + "% smaller)");

Unpickler unpickler = new Unpickler();

long t3 = System.nanoTime();
List<String> without = (List<String>) unpickler.loads(noValueCompare);
long t4 = System.nanoTime();
List<String> with = (List<String>) unpickler.loads(withValueCompare);
long t5 = System.nanoTime();

System.out.println("Whole List equality: ==: " + (without == with) + ", .equals(): " + (without.equals(with)));

System.out.println(
"Two equal elements equality without valueCompare: ==: " + (without.get(107) == without.get(259))
+ ", .equals(): " + (without.get(107).equals(without.get(259))));
System.out.println("Two equal elements equality with valueCompare: ==: " + (with.get(107) == with.get(259))
+ ", .equals(): " + (with.get(107).equals(with.get(259))));

System.out.println("Pickling without valueCompare took " + (t1 - t0) / 1000000.0 + "ms");
System.out.println("Pickling with valueCompare took " + (t2 - t1) / 1000000.0 + "ms");

System.out.println("Unpickling without valueCompare took " + (t4 - t3) / 1000000.0 + "ms");
System.out.println("Unpickling with valueCompare took " + (t5 - t4) / 1000000.0 + "ms");

}

}

0 comments on commit f996dde

Please sign in to comment.