You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@harmony.apache.org by "Evgeniya Maenkova (JIRA)" <ji...@apache.org> on 2007/01/30 14:42:33 UTC

[jira] Created: (HARMONY-3086) [math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal

[math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal
---------------------------------------------------------------------

                 Key: HARMONY-3086
                 URL: https://issues.apache.org/jira/browse/HARMONY-3086
             Project: Harmony
          Issue Type: Improvement
          Components: Classlib
         Environment: Platform independent
            Reporter: Evgeniya Maenkova


As BigDecimal/BigInteger.hashCode isn't specified I belive we can do some improvements in this arrea.

1)BigInteger.hashCode computes a hash code accordingly to the latest digit only. We could take all digits into account.
2)BigDecimal.hashCode uses shift, so BigInteger's hashCode has pretty small influence on BigDecimal's hashCode.
 public int hashCode() {
        /* Take the 24 trailing bits of BigInteger hashcode
         * and the 8 trailing bits of scale. */
        return ((getUnscaledValue().hashCode() << 24) | (0xFF & scale));
    }
3) when we are dealing with "small" BigDecimal I believe it's possible to compute hash code more effectively without redudant objects creation (I mean BigInteger).
4) I've written minimal test case with "big" BigDecimal as keys in some hashMap(to be attached, and see please below) and there the results (my laptop :), pretty approximately, of course):
RI:
--
different hash coodes: 45000
time6173
--
current java.math:
different hash coodes: 906
time82239

--patched java.math:
different hash coodes: 45000
time6344

(This test shows so big difference in time and hash code distribution.
5)I get 1 failed test with this patch:org.apache.harmony.tests.java.math.BigIntegerHashCodeTest. Test is looking doubtful as (a) hash code isn;t specified so we shouldn't require this behavior; (b) It fails on RI also. So I would propose to remove this test at all.

-    public void testUnequalObjectsEqual() {
-        byte aBytes[] = {56, 100, -2, -76, 98, 54, 19, 3, -15, 45, 89, -111, 69, 103, 8, -9};
-        byte bBytes[] = {56, 100, -2, -76, 89, 45, 91, 3, -15, 45, 89, -111, 69, 103, 8, -9};
-        int aSign = 1;
-        BigInteger aNumber = new BigInteger(aSign, aBytes);
-        BigInteger bNumber = new BigInteger(aSign, bBytes);
-        int code1 = aNumber.hashCode();
-        int code2 = bNumber.hashCode();
-        if (!aNumber.equals(bNumber)) {
-            assertTrue("hash codes for these unequal objects should be equal", code1 == code2);
-        }
-    }
+    }      


Test  case mentioned above:
----
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Random;

public class HashMapTest {
	static class MyBigDecimal extends BigDecimal {
		public MyBigDecimal(String s) {
			super(s);
		}
		static long hashCodeCallsCounter;
		static long equalsCallsCounter;
		@Override
		public int hashCode() {
			hashCodeCallsCounter ++;
			return super.hashCode();
		}
		
		@Override
		public boolean equals(Object arg0) {
			equalsCallsCounter ++;
			return super.equals(arg0);
		}
		
		static void reset() {
			equalsCallsCounter = 0;
			hashCodeCallsCounter = 0;
		}
	}
	
	
	static MyBigDecimal[][][] keys;
	static int[] hashcodes;
	public static HashMap createRatesMap(int dim1, int dim2,
            int dim3, BigDecimal value1) {
        HashMap map = new HashMap();
        keys = new MyBigDecimal[dim1][dim2][dim3];
        StringBuffer sb = new StringBuffer();
        NumberFormat nf = NumberFormat.getNumberInstance();
        nf.setMinimumIntegerDigits(3);
        hashcodes = new int[dim1 * dim2 * dim3];
        int ind = 0;
        for (int i = 0; i < dim1; i ++){
            for (int j = 0; j < dim2; j ++) {
                for (int k = 0; k < dim3; k ++) {
                    long res = i * 10000 + j * 100 + k;
                    int scale = 3 + ((i + j + k) % 5);
                    MyBigDecimal key = new MyBigDecimal(res + "." + res);
                    keys[i][j][k] = key;
                    hashcodes[ind ++] = key.hashCode();
                    map.put(key, value1.setScale(scale, BigDecimal.ROUND_DOWN));
                }
            }
        }
        return map;
    }	
	
	public static void main(String[] args) {
		HashMap map = createRatesMap(300, 10, 15, BigDecimal.ONE);
		Arrays.sort(hashcodes);
		int currentValue = 0;
		int currentIndex = 0;
		int curLength = 0;
		int[] value = new int[hashcodes.length];
		int[] counters = new int[hashcodes.length];
		
		for (int i = 0; i < hashcodes.length; i ++) {
			if (hashcodes[i] == currentValue) {
				currentIndex++;
				counters[curLength - 1] = currentIndex;				
			} else {				
				currentIndex = 1;
				currentValue = hashcodes[i];
				value[curLength] = currentValue;
				counters[curLength] = 1;
				curLength++;
			}
		}		
		
		MyBigDecimal.reset();		
		Random random = new Random();	
		long start = System.currentTimeMillis();
		for (int i = 0; i < 10000000; i ++) {
		    map.get(keys[random.nextInt(300)][random.nextInt(10)][random.nextInt(15)]);
		}
		System.out.println("different hash coodes: " + curLength);
		System.out.println("time" + (System.currentTimeMillis() - start));		
		
	}
}





-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.


[jira] Commented: (HARMONY-3086) [math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal

Posted by "Alexey Petrenko (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/HARMONY-3086?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#action_12468655 ] 

Alexey Petrenko commented on HARMONY-3086:
------------------------------------------

Evgeniya,

thanks for your investigation and great patch!

But your your patch causes the failure of one of security tests on my W2K Server:
org.apache.harmony.security.tests.java.security.spec.ECPointTest.testHashCode01
N/A

junit.framework.AssertionFailedError
at org.apache.harmony.security.tests.java.security.spec.ECPointTest.testHashCode01(ECPointTest.java:210)
at java.lang.reflect.AccessibleObject.invokeV(AccessibleObject.java:25)

Can you please check it out?

> [math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal
> ---------------------------------------------------------------------
>
>                 Key: HARMONY-3086
>                 URL: https://issues.apache.org/jira/browse/HARMONY-3086
>             Project: Harmony
>          Issue Type: Improvement
>          Components: Classlib
>         Environment: Platform independent
>            Reporter: Evgeniya Maenkova
>         Assigned To: Alexey Petrenko
>         Attachments: HashMapTest.java, math.patch
>
>
> As BigDecimal/BigInteger.hashCode isn't specified I belive we can do some improvements in this arrea.
> 1)BigInteger.hashCode computes a hash code accordingly to the latest digit only. We could take all digits into account.
> 2)BigDecimal.hashCode uses shift, so BigInteger's hashCode has pretty small influence on BigDecimal's hashCode.
>  public int hashCode() {
>         /* Take the 24 trailing bits of BigInteger hashcode
>          * and the 8 trailing bits of scale. */
>         return ((getUnscaledValue().hashCode() << 24) | (0xFF & scale));
>     }
> 3) when we are dealing with "small" BigDecimal I believe it's possible to compute hash code more effectively without redudant objects creation (I mean BigInteger).
> 4) I've written minimal test case with "big" BigDecimal as keys in some hashMap(to be attached, and see please below) and there the results (my laptop :), pretty approximately, of course):
> RI:
> --
> different hash coodes: 45000
> time6173
> --
> current java.math:
> different hash coodes: 906
> time82239
> --patched java.math:
> different hash coodes: 45000
> time6344
> (This test shows so big difference in time and hash code distribution.
> 5)I get 1 failed test with this patch:org.apache.harmony.tests.java.math.BigIntegerHashCodeTest. Test is looking doubtful as (a) hash code isn;t specified so we shouldn't require this behavior; (b) It fails on RI also. So I would propose to remove this test at all.
> -    public void testUnequalObjectsEqual() {
> -        byte aBytes[] = {56, 100, -2, -76, 98, 54, 19, 3, -15, 45, 89, -111, 69, 103, 8, -9};
> -        byte bBytes[] = {56, 100, -2, -76, 89, 45, 91, 3, -15, 45, 89, -111, 69, 103, 8, -9};
> -        int aSign = 1;
> -        BigInteger aNumber = new BigInteger(aSign, aBytes);
> -        BigInteger bNumber = new BigInteger(aSign, bBytes);
> -        int code1 = aNumber.hashCode();
> -        int code2 = bNumber.hashCode();
> -        if (!aNumber.equals(bNumber)) {
> -            assertTrue("hash codes for these unequal objects should be equal", code1 == code2);
> -        }
> -    }
> +    }      
> Test  case mentioned above:
> ----
> import java.math.BigDecimal;
> import java.text.NumberFormat;
> import java.util.Arrays;
> import java.util.HashMap;
> import java.util.Random;
> public class HashMapTest {
> 	static class MyBigDecimal extends BigDecimal {
> 		public MyBigDecimal(String s) {
> 			super(s);
> 		}
> 		static long hashCodeCallsCounter;
> 		static long equalsCallsCounter;
> 		@Override
> 		public int hashCode() {
> 			hashCodeCallsCounter ++;
> 			return super.hashCode();
> 		}
> 		
> 		@Override
> 		public boolean equals(Object arg0) {
> 			equalsCallsCounter ++;
> 			return super.equals(arg0);
> 		}
> 		
> 		static void reset() {
> 			equalsCallsCounter = 0;
> 			hashCodeCallsCounter = 0;
> 		}
> 	}
> 	
> 	
> 	static MyBigDecimal[][][] keys;
> 	static int[] hashcodes;
> 	public static HashMap createRatesMap(int dim1, int dim2,
>             int dim3, BigDecimal value1) {
>         HashMap map = new HashMap();
>         keys = new MyBigDecimal[dim1][dim2][dim3];
>         StringBuffer sb = new StringBuffer();
>         NumberFormat nf = NumberFormat.getNumberInstance();
>         nf.setMinimumIntegerDigits(3);
>         hashcodes = new int[dim1 * dim2 * dim3];
>         int ind = 0;
>         for (int i = 0; i < dim1; i ++){
>             for (int j = 0; j < dim2; j ++) {
>                 for (int k = 0; k < dim3; k ++) {
>                     long res = i * 10000 + j * 100 + k;
>                     int scale = 3 + ((i + j + k) % 5);
>                     MyBigDecimal key = new MyBigDecimal(res + "." + res);
>                     keys[i][j][k] = key;
>                     hashcodes[ind ++] = key.hashCode();
>                     map.put(key, value1.setScale(scale, BigDecimal.ROUND_DOWN));
>                 }
>             }
>         }
>         return map;
>     }	
> 	
> 	public static void main(String[] args) {
> 		HashMap map = createRatesMap(300, 10, 15, BigDecimal.ONE);
> 		Arrays.sort(hashcodes);
> 		int currentValue = 0;
> 		int currentIndex = 0;
> 		int curLength = 0;
> 		int[] value = new int[hashcodes.length];
> 		int[] counters = new int[hashcodes.length];
> 		
> 		for (int i = 0; i < hashcodes.length; i ++) {
> 			if (hashcodes[i] == currentValue) {
> 				currentIndex++;
> 				counters[curLength - 1] = currentIndex;				
> 			} else {				
> 				currentIndex = 1;
> 				currentValue = hashcodes[i];
> 				value[curLength] = currentValue;
> 				counters[curLength] = 1;
> 				curLength++;
> 			}
> 		}		
> 		
> 		MyBigDecimal.reset();		
> 		Random random = new Random();	
> 		long start = System.currentTimeMillis();
> 		for (int i = 0; i < 10000000; i ++) {
> 		    map.get(keys[random.nextInt(300)][random.nextInt(10)][random.nextInt(15)]);
> 		}
> 		System.out.println("different hash coodes: " + curLength);
> 		System.out.println("time" + (System.currentTimeMillis() - start));		
> 		
> 	}
> }

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.


[jira] Updated: (HARMONY-3086) [math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal

Posted by "Evgeniya Maenkova (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/HARMONY-3086?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Evgeniya Maenkova updated HARMONY-3086:
---------------------------------------

    Attachment: math.patch

patch on BigDecimal, BigInteger and BigIntegerHashCodeTest.

> [math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal
> ---------------------------------------------------------------------
>
>                 Key: HARMONY-3086
>                 URL: https://issues.apache.org/jira/browse/HARMONY-3086
>             Project: Harmony
>          Issue Type: Improvement
>          Components: Classlib
>         Environment: Platform independent
>            Reporter: Evgeniya Maenkova
>         Attachments: HashMapTest.java, math.patch
>
>
> As BigDecimal/BigInteger.hashCode isn't specified I belive we can do some improvements in this arrea.
> 1)BigInteger.hashCode computes a hash code accordingly to the latest digit only. We could take all digits into account.
> 2)BigDecimal.hashCode uses shift, so BigInteger's hashCode has pretty small influence on BigDecimal's hashCode.
>  public int hashCode() {
>         /* Take the 24 trailing bits of BigInteger hashcode
>          * and the 8 trailing bits of scale. */
>         return ((getUnscaledValue().hashCode() << 24) | (0xFF & scale));
>     }
> 3) when we are dealing with "small" BigDecimal I believe it's possible to compute hash code more effectively without redudant objects creation (I mean BigInteger).
> 4) I've written minimal test case with "big" BigDecimal as keys in some hashMap(to be attached, and see please below) and there the results (my laptop :), pretty approximately, of course):
> RI:
> --
> different hash coodes: 45000
> time6173
> --
> current java.math:
> different hash coodes: 906
> time82239
> --patched java.math:
> different hash coodes: 45000
> time6344
> (This test shows so big difference in time and hash code distribution.
> 5)I get 1 failed test with this patch:org.apache.harmony.tests.java.math.BigIntegerHashCodeTest. Test is looking doubtful as (a) hash code isn;t specified so we shouldn't require this behavior; (b) It fails on RI also. So I would propose to remove this test at all.
> -    public void testUnequalObjectsEqual() {
> -        byte aBytes[] = {56, 100, -2, -76, 98, 54, 19, 3, -15, 45, 89, -111, 69, 103, 8, -9};
> -        byte bBytes[] = {56, 100, -2, -76, 89, 45, 91, 3, -15, 45, 89, -111, 69, 103, 8, -9};
> -        int aSign = 1;
> -        BigInteger aNumber = new BigInteger(aSign, aBytes);
> -        BigInteger bNumber = new BigInteger(aSign, bBytes);
> -        int code1 = aNumber.hashCode();
> -        int code2 = bNumber.hashCode();
> -        if (!aNumber.equals(bNumber)) {
> -            assertTrue("hash codes for these unequal objects should be equal", code1 == code2);
> -        }
> -    }
> +    }      
> Test  case mentioned above:
> ----
> import java.math.BigDecimal;
> import java.text.NumberFormat;
> import java.util.Arrays;
> import java.util.HashMap;
> import java.util.Random;
> public class HashMapTest {
> 	static class MyBigDecimal extends BigDecimal {
> 		public MyBigDecimal(String s) {
> 			super(s);
> 		}
> 		static long hashCodeCallsCounter;
> 		static long equalsCallsCounter;
> 		@Override
> 		public int hashCode() {
> 			hashCodeCallsCounter ++;
> 			return super.hashCode();
> 		}
> 		
> 		@Override
> 		public boolean equals(Object arg0) {
> 			equalsCallsCounter ++;
> 			return super.equals(arg0);
> 		}
> 		
> 		static void reset() {
> 			equalsCallsCounter = 0;
> 			hashCodeCallsCounter = 0;
> 		}
> 	}
> 	
> 	
> 	static MyBigDecimal[][][] keys;
> 	static int[] hashcodes;
> 	public static HashMap createRatesMap(int dim1, int dim2,
>             int dim3, BigDecimal value1) {
>         HashMap map = new HashMap();
>         keys = new MyBigDecimal[dim1][dim2][dim3];
>         StringBuffer sb = new StringBuffer();
>         NumberFormat nf = NumberFormat.getNumberInstance();
>         nf.setMinimumIntegerDigits(3);
>         hashcodes = new int[dim1 * dim2 * dim3];
>         int ind = 0;
>         for (int i = 0; i < dim1; i ++){
>             for (int j = 0; j < dim2; j ++) {
>                 for (int k = 0; k < dim3; k ++) {
>                     long res = i * 10000 + j * 100 + k;
>                     int scale = 3 + ((i + j + k) % 5);
>                     MyBigDecimal key = new MyBigDecimal(res + "." + res);
>                     keys[i][j][k] = key;
>                     hashcodes[ind ++] = key.hashCode();
>                     map.put(key, value1.setScale(scale, BigDecimal.ROUND_DOWN));
>                 }
>             }
>         }
>         return map;
>     }	
> 	
> 	public static void main(String[] args) {
> 		HashMap map = createRatesMap(300, 10, 15, BigDecimal.ONE);
> 		Arrays.sort(hashcodes);
> 		int currentValue = 0;
> 		int currentIndex = 0;
> 		int curLength = 0;
> 		int[] value = new int[hashcodes.length];
> 		int[] counters = new int[hashcodes.length];
> 		
> 		for (int i = 0; i < hashcodes.length; i ++) {
> 			if (hashcodes[i] == currentValue) {
> 				currentIndex++;
> 				counters[curLength - 1] = currentIndex;				
> 			} else {				
> 				currentIndex = 1;
> 				currentValue = hashcodes[i];
> 				value[curLength] = currentValue;
> 				counters[curLength] = 1;
> 				curLength++;
> 			}
> 		}		
> 		
> 		MyBigDecimal.reset();		
> 		Random random = new Random();	
> 		long start = System.currentTimeMillis();
> 		for (int i = 0; i < 10000000; i ++) {
> 		    map.get(keys[random.nextInt(300)][random.nextInt(10)][random.nextInt(15)]);
> 		}
> 		System.out.println("different hash coodes: " + curLength);
> 		System.out.println("time" + (System.currentTimeMillis() - start));		
> 		
> 	}
> }

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.


[jira] Assigned: (HARMONY-3086) [math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal

Posted by "Alexey Petrenko (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/HARMONY-3086?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Alexey Petrenko reassigned HARMONY-3086:
----------------------------------------

    Assignee: Alexey Petrenko

> [math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal
> ---------------------------------------------------------------------
>
>                 Key: HARMONY-3086
>                 URL: https://issues.apache.org/jira/browse/HARMONY-3086
>             Project: Harmony
>          Issue Type: Improvement
>          Components: Classlib
>         Environment: Platform independent
>            Reporter: Evgeniya Maenkova
>         Assigned To: Alexey Petrenko
>         Attachments: HashMapTest.java, math.patch
>
>
> As BigDecimal/BigInteger.hashCode isn't specified I belive we can do some improvements in this arrea.
> 1)BigInteger.hashCode computes a hash code accordingly to the latest digit only. We could take all digits into account.
> 2)BigDecimal.hashCode uses shift, so BigInteger's hashCode has pretty small influence on BigDecimal's hashCode.
>  public int hashCode() {
>         /* Take the 24 trailing bits of BigInteger hashcode
>          * and the 8 trailing bits of scale. */
>         return ((getUnscaledValue().hashCode() << 24) | (0xFF & scale));
>     }
> 3) when we are dealing with "small" BigDecimal I believe it's possible to compute hash code more effectively without redudant objects creation (I mean BigInteger).
> 4) I've written minimal test case with "big" BigDecimal as keys in some hashMap(to be attached, and see please below) and there the results (my laptop :), pretty approximately, of course):
> RI:
> --
> different hash coodes: 45000
> time6173
> --
> current java.math:
> different hash coodes: 906
> time82239
> --patched java.math:
> different hash coodes: 45000
> time6344
> (This test shows so big difference in time and hash code distribution.
> 5)I get 1 failed test with this patch:org.apache.harmony.tests.java.math.BigIntegerHashCodeTest. Test is looking doubtful as (a) hash code isn;t specified so we shouldn't require this behavior; (b) It fails on RI also. So I would propose to remove this test at all.
> -    public void testUnequalObjectsEqual() {
> -        byte aBytes[] = {56, 100, -2, -76, 98, 54, 19, 3, -15, 45, 89, -111, 69, 103, 8, -9};
> -        byte bBytes[] = {56, 100, -2, -76, 89, 45, 91, 3, -15, 45, 89, -111, 69, 103, 8, -9};
> -        int aSign = 1;
> -        BigInteger aNumber = new BigInteger(aSign, aBytes);
> -        BigInteger bNumber = new BigInteger(aSign, bBytes);
> -        int code1 = aNumber.hashCode();
> -        int code2 = bNumber.hashCode();
> -        if (!aNumber.equals(bNumber)) {
> -            assertTrue("hash codes for these unequal objects should be equal", code1 == code2);
> -        }
> -    }
> +    }      
> Test  case mentioned above:
> ----
> import java.math.BigDecimal;
> import java.text.NumberFormat;
> import java.util.Arrays;
> import java.util.HashMap;
> import java.util.Random;
> public class HashMapTest {
> 	static class MyBigDecimal extends BigDecimal {
> 		public MyBigDecimal(String s) {
> 			super(s);
> 		}
> 		static long hashCodeCallsCounter;
> 		static long equalsCallsCounter;
> 		@Override
> 		public int hashCode() {
> 			hashCodeCallsCounter ++;
> 			return super.hashCode();
> 		}
> 		
> 		@Override
> 		public boolean equals(Object arg0) {
> 			equalsCallsCounter ++;
> 			return super.equals(arg0);
> 		}
> 		
> 		static void reset() {
> 			equalsCallsCounter = 0;
> 			hashCodeCallsCounter = 0;
> 		}
> 	}
> 	
> 	
> 	static MyBigDecimal[][][] keys;
> 	static int[] hashcodes;
> 	public static HashMap createRatesMap(int dim1, int dim2,
>             int dim3, BigDecimal value1) {
>         HashMap map = new HashMap();
>         keys = new MyBigDecimal[dim1][dim2][dim3];
>         StringBuffer sb = new StringBuffer();
>         NumberFormat nf = NumberFormat.getNumberInstance();
>         nf.setMinimumIntegerDigits(3);
>         hashcodes = new int[dim1 * dim2 * dim3];
>         int ind = 0;
>         for (int i = 0; i < dim1; i ++){
>             for (int j = 0; j < dim2; j ++) {
>                 for (int k = 0; k < dim3; k ++) {
>                     long res = i * 10000 + j * 100 + k;
>                     int scale = 3 + ((i + j + k) % 5);
>                     MyBigDecimal key = new MyBigDecimal(res + "." + res);
>                     keys[i][j][k] = key;
>                     hashcodes[ind ++] = key.hashCode();
>                     map.put(key, value1.setScale(scale, BigDecimal.ROUND_DOWN));
>                 }
>             }
>         }
>         return map;
>     }	
> 	
> 	public static void main(String[] args) {
> 		HashMap map = createRatesMap(300, 10, 15, BigDecimal.ONE);
> 		Arrays.sort(hashcodes);
> 		int currentValue = 0;
> 		int currentIndex = 0;
> 		int curLength = 0;
> 		int[] value = new int[hashcodes.length];
> 		int[] counters = new int[hashcodes.length];
> 		
> 		for (int i = 0; i < hashcodes.length; i ++) {
> 			if (hashcodes[i] == currentValue) {
> 				currentIndex++;
> 				counters[curLength - 1] = currentIndex;				
> 			} else {				
> 				currentIndex = 1;
> 				currentValue = hashcodes[i];
> 				value[curLength] = currentValue;
> 				counters[curLength] = 1;
> 				curLength++;
> 			}
> 		}		
> 		
> 		MyBigDecimal.reset();		
> 		Random random = new Random();	
> 		long start = System.currentTimeMillis();
> 		for (int i = 0; i < 10000000; i ++) {
> 		    map.get(keys[random.nextInt(300)][random.nextInt(10)][random.nextInt(15)]);
> 		}
> 		System.out.println("different hash coodes: " + curLength);
> 		System.out.println("time" + (System.currentTimeMillis() - start));		
> 		
> 	}
> }

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.


[jira] Updated: (HARMONY-3086) [math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal

Posted by "Evgeniya Maenkova (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/HARMONY-3086?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Evgeniya Maenkova updated HARMONY-3086:
---------------------------------------

    Attachment: HashMapTest.java

Test 

> [math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal
> ---------------------------------------------------------------------
>
>                 Key: HARMONY-3086
>                 URL: https://issues.apache.org/jira/browse/HARMONY-3086
>             Project: Harmony
>          Issue Type: Improvement
>          Components: Classlib
>         Environment: Platform independent
>            Reporter: Evgeniya Maenkova
>         Attachments: HashMapTest.java
>
>
> As BigDecimal/BigInteger.hashCode isn't specified I belive we can do some improvements in this arrea.
> 1)BigInteger.hashCode computes a hash code accordingly to the latest digit only. We could take all digits into account.
> 2)BigDecimal.hashCode uses shift, so BigInteger's hashCode has pretty small influence on BigDecimal's hashCode.
>  public int hashCode() {
>         /* Take the 24 trailing bits of BigInteger hashcode
>          * and the 8 trailing bits of scale. */
>         return ((getUnscaledValue().hashCode() << 24) | (0xFF & scale));
>     }
> 3) when we are dealing with "small" BigDecimal I believe it's possible to compute hash code more effectively without redudant objects creation (I mean BigInteger).
> 4) I've written minimal test case with "big" BigDecimal as keys in some hashMap(to be attached, and see please below) and there the results (my laptop :), pretty approximately, of course):
> RI:
> --
> different hash coodes: 45000
> time6173
> --
> current java.math:
> different hash coodes: 906
> time82239
> --patched java.math:
> different hash coodes: 45000
> time6344
> (This test shows so big difference in time and hash code distribution.
> 5)I get 1 failed test with this patch:org.apache.harmony.tests.java.math.BigIntegerHashCodeTest. Test is looking doubtful as (a) hash code isn;t specified so we shouldn't require this behavior; (b) It fails on RI also. So I would propose to remove this test at all.
> -    public void testUnequalObjectsEqual() {
> -        byte aBytes[] = {56, 100, -2, -76, 98, 54, 19, 3, -15, 45, 89, -111, 69, 103, 8, -9};
> -        byte bBytes[] = {56, 100, -2, -76, 89, 45, 91, 3, -15, 45, 89, -111, 69, 103, 8, -9};
> -        int aSign = 1;
> -        BigInteger aNumber = new BigInteger(aSign, aBytes);
> -        BigInteger bNumber = new BigInteger(aSign, bBytes);
> -        int code1 = aNumber.hashCode();
> -        int code2 = bNumber.hashCode();
> -        if (!aNumber.equals(bNumber)) {
> -            assertTrue("hash codes for these unequal objects should be equal", code1 == code2);
> -        }
> -    }
> +    }      
> Test  case mentioned above:
> ----
> import java.math.BigDecimal;
> import java.text.NumberFormat;
> import java.util.Arrays;
> import java.util.HashMap;
> import java.util.Random;
> public class HashMapTest {
> 	static class MyBigDecimal extends BigDecimal {
> 		public MyBigDecimal(String s) {
> 			super(s);
> 		}
> 		static long hashCodeCallsCounter;
> 		static long equalsCallsCounter;
> 		@Override
> 		public int hashCode() {
> 			hashCodeCallsCounter ++;
> 			return super.hashCode();
> 		}
> 		
> 		@Override
> 		public boolean equals(Object arg0) {
> 			equalsCallsCounter ++;
> 			return super.equals(arg0);
> 		}
> 		
> 		static void reset() {
> 			equalsCallsCounter = 0;
> 			hashCodeCallsCounter = 0;
> 		}
> 	}
> 	
> 	
> 	static MyBigDecimal[][][] keys;
> 	static int[] hashcodes;
> 	public static HashMap createRatesMap(int dim1, int dim2,
>             int dim3, BigDecimal value1) {
>         HashMap map = new HashMap();
>         keys = new MyBigDecimal[dim1][dim2][dim3];
>         StringBuffer sb = new StringBuffer();
>         NumberFormat nf = NumberFormat.getNumberInstance();
>         nf.setMinimumIntegerDigits(3);
>         hashcodes = new int[dim1 * dim2 * dim3];
>         int ind = 0;
>         for (int i = 0; i < dim1; i ++){
>             for (int j = 0; j < dim2; j ++) {
>                 for (int k = 0; k < dim3; k ++) {
>                     long res = i * 10000 + j * 100 + k;
>                     int scale = 3 + ((i + j + k) % 5);
>                     MyBigDecimal key = new MyBigDecimal(res + "." + res);
>                     keys[i][j][k] = key;
>                     hashcodes[ind ++] = key.hashCode();
>                     map.put(key, value1.setScale(scale, BigDecimal.ROUND_DOWN));
>                 }
>             }
>         }
>         return map;
>     }	
> 	
> 	public static void main(String[] args) {
> 		HashMap map = createRatesMap(300, 10, 15, BigDecimal.ONE);
> 		Arrays.sort(hashcodes);
> 		int currentValue = 0;
> 		int currentIndex = 0;
> 		int curLength = 0;
> 		int[] value = new int[hashcodes.length];
> 		int[] counters = new int[hashcodes.length];
> 		
> 		for (int i = 0; i < hashcodes.length; i ++) {
> 			if (hashcodes[i] == currentValue) {
> 				currentIndex++;
> 				counters[curLength - 1] = currentIndex;				
> 			} else {				
> 				currentIndex = 1;
> 				currentValue = hashcodes[i];
> 				value[curLength] = currentValue;
> 				counters[curLength] = 1;
> 				curLength++;
> 			}
> 		}		
> 		
> 		MyBigDecimal.reset();		
> 		Random random = new Random();	
> 		long start = System.currentTimeMillis();
> 		for (int i = 0; i < 10000000; i ++) {
> 		    map.get(keys[random.nextInt(300)][random.nextInt(10)][random.nextInt(15)]);
> 		}
> 		System.out.println("different hash coodes: " + curLength);
> 		System.out.println("time" + (System.currentTimeMillis() - start));		
> 		
> 	}
> }

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.


[jira] Closed: (HARMONY-3086) [math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal

Posted by "Evgeniya Maenkova (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/HARMONY-3086?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Evgeniya Maenkova closed HARMONY-3086.
--------------------------------------


Thanks, Alexey: works fine.

> [math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal
> ---------------------------------------------------------------------
>
>                 Key: HARMONY-3086
>                 URL: https://issues.apache.org/jira/browse/HARMONY-3086
>             Project: Harmony
>          Issue Type: Improvement
>          Components: Classlib
>         Environment: Platform independent
>            Reporter: Evgeniya Maenkova
>         Assigned To: Alexey Petrenko
>         Attachments: HashMapTest.java, math.patch, math.patch, math.patch
>
>
> As BigDecimal/BigInteger.hashCode isn't specified I belive we can do some improvements in this arrea.
> 1)BigInteger.hashCode computes a hash code accordingly to the latest digit only. We could take all digits into account.
> 2)BigDecimal.hashCode uses shift, so BigInteger's hashCode has pretty small influence on BigDecimal's hashCode.
>  public int hashCode() {
>         /* Take the 24 trailing bits of BigInteger hashcode
>          * and the 8 trailing bits of scale. */
>         return ((getUnscaledValue().hashCode() << 24) | (0xFF & scale));
>     }
> 3) when we are dealing with "small" BigDecimal I believe it's possible to compute hash code more effectively without redudant objects creation (I mean BigInteger).
> 4) I've written minimal test case with "big" BigDecimal as keys in some hashMap(to be attached, and see please below) and there the results (my laptop :), pretty approximately, of course):
> RI:
> --
> different hash coodes: 45000
> time6173
> --
> current java.math:
> different hash coodes: 906
> time82239
> --patched java.math:
> different hash coodes: 45000
> time6344
> (This test shows so big difference in time and hash code distribution.
> 5)I get 1 failed test with this patch:org.apache.harmony.tests.java.math.BigIntegerHashCodeTest. Test is looking doubtful as (a) hash code isn;t specified so we shouldn't require this behavior; (b) It fails on RI also. So I would propose to remove this test at all.
> -    public void testUnequalObjectsEqual() {
> -        byte aBytes[] = {56, 100, -2, -76, 98, 54, 19, 3, -15, 45, 89, -111, 69, 103, 8, -9};
> -        byte bBytes[] = {56, 100, -2, -76, 89, 45, 91, 3, -15, 45, 89, -111, 69, 103, 8, -9};
> -        int aSign = 1;
> -        BigInteger aNumber = new BigInteger(aSign, aBytes);
> -        BigInteger bNumber = new BigInteger(aSign, bBytes);
> -        int code1 = aNumber.hashCode();
> -        int code2 = bNumber.hashCode();
> -        if (!aNumber.equals(bNumber)) {
> -            assertTrue("hash codes for these unequal objects should be equal", code1 == code2);
> -        }
> -    }
> +    }      
> Test  case mentioned above:
> ----
> import java.math.BigDecimal;
> import java.text.NumberFormat;
> import java.util.Arrays;
> import java.util.HashMap;
> import java.util.Random;
> public class HashMapTest {
> 	static class MyBigDecimal extends BigDecimal {
> 		public MyBigDecimal(String s) {
> 			super(s);
> 		}
> 		static long hashCodeCallsCounter;
> 		static long equalsCallsCounter;
> 		@Override
> 		public int hashCode() {
> 			hashCodeCallsCounter ++;
> 			return super.hashCode();
> 		}
> 		
> 		@Override
> 		public boolean equals(Object arg0) {
> 			equalsCallsCounter ++;
> 			return super.equals(arg0);
> 		}
> 		
> 		static void reset() {
> 			equalsCallsCounter = 0;
> 			hashCodeCallsCounter = 0;
> 		}
> 	}
> 	
> 	
> 	static MyBigDecimal[][][] keys;
> 	static int[] hashcodes;
> 	public static HashMap createRatesMap(int dim1, int dim2,
>             int dim3, BigDecimal value1) {
>         HashMap map = new HashMap();
>         keys = new MyBigDecimal[dim1][dim2][dim3];
>         StringBuffer sb = new StringBuffer();
>         NumberFormat nf = NumberFormat.getNumberInstance();
>         nf.setMinimumIntegerDigits(3);
>         hashcodes = new int[dim1 * dim2 * dim3];
>         int ind = 0;
>         for (int i = 0; i < dim1; i ++){
>             for (int j = 0; j < dim2; j ++) {
>                 for (int k = 0; k < dim3; k ++) {
>                     long res = i * 10000 + j * 100 + k;
>                     int scale = 3 + ((i + j + k) % 5);
>                     MyBigDecimal key = new MyBigDecimal(res + "." + res);
>                     keys[i][j][k] = key;
>                     hashcodes[ind ++] = key.hashCode();
>                     map.put(key, value1.setScale(scale, BigDecimal.ROUND_DOWN));
>                 }
>             }
>         }
>         return map;
>     }	
> 	
> 	public static void main(String[] args) {
> 		HashMap map = createRatesMap(300, 10, 15, BigDecimal.ONE);
> 		Arrays.sort(hashcodes);
> 		int currentValue = 0;
> 		int currentIndex = 0;
> 		int curLength = 0;
> 		int[] value = new int[hashcodes.length];
> 		int[] counters = new int[hashcodes.length];
> 		
> 		for (int i = 0; i < hashcodes.length; i ++) {
> 			if (hashcodes[i] == currentValue) {
> 				currentIndex++;
> 				counters[curLength - 1] = currentIndex;				
> 			} else {				
> 				currentIndex = 1;
> 				currentValue = hashcodes[i];
> 				value[curLength] = currentValue;
> 				counters[curLength] = 1;
> 				curLength++;
> 			}
> 		}		
> 		
> 		MyBigDecimal.reset();		
> 		Random random = new Random();	
> 		long start = System.currentTimeMillis();
> 		for (int i = 0; i < 10000000; i ++) {
> 		    map.get(keys[random.nextInt(300)][random.nextInt(10)][random.nextInt(15)]);
> 		}
> 		System.out.println("different hash coodes: " + curLength);
> 		System.out.println("time" + (System.currentTimeMillis() - start));		
> 		
> 	}
> }

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.


[jira] Resolved: (HARMONY-3086) [math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal

Posted by "Alexey Petrenko (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/HARMONY-3086?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Alexey Petrenko resolved HARMONY-3086.
--------------------------------------

    Resolution: Fixed

The patch has been committed. Evgeniya, please verify.

> [math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal
> ---------------------------------------------------------------------
>
>                 Key: HARMONY-3086
>                 URL: https://issues.apache.org/jira/browse/HARMONY-3086
>             Project: Harmony
>          Issue Type: Improvement
>          Components: Classlib
>         Environment: Platform independent
>            Reporter: Evgeniya Maenkova
>         Assigned To: Alexey Petrenko
>         Attachments: HashMapTest.java, math.patch, math.patch, math.patch
>
>
> As BigDecimal/BigInteger.hashCode isn't specified I belive we can do some improvements in this arrea.
> 1)BigInteger.hashCode computes a hash code accordingly to the latest digit only. We could take all digits into account.
> 2)BigDecimal.hashCode uses shift, so BigInteger's hashCode has pretty small influence on BigDecimal's hashCode.
>  public int hashCode() {
>         /* Take the 24 trailing bits of BigInteger hashcode
>          * and the 8 trailing bits of scale. */
>         return ((getUnscaledValue().hashCode() << 24) | (0xFF & scale));
>     }
> 3) when we are dealing with "small" BigDecimal I believe it's possible to compute hash code more effectively without redudant objects creation (I mean BigInteger).
> 4) I've written minimal test case with "big" BigDecimal as keys in some hashMap(to be attached, and see please below) and there the results (my laptop :), pretty approximately, of course):
> RI:
> --
> different hash coodes: 45000
> time6173
> --
> current java.math:
> different hash coodes: 906
> time82239
> --patched java.math:
> different hash coodes: 45000
> time6344
> (This test shows so big difference in time and hash code distribution.
> 5)I get 1 failed test with this patch:org.apache.harmony.tests.java.math.BigIntegerHashCodeTest. Test is looking doubtful as (a) hash code isn;t specified so we shouldn't require this behavior; (b) It fails on RI also. So I would propose to remove this test at all.
> -    public void testUnequalObjectsEqual() {
> -        byte aBytes[] = {56, 100, -2, -76, 98, 54, 19, 3, -15, 45, 89, -111, 69, 103, 8, -9};
> -        byte bBytes[] = {56, 100, -2, -76, 89, 45, 91, 3, -15, 45, 89, -111, 69, 103, 8, -9};
> -        int aSign = 1;
> -        BigInteger aNumber = new BigInteger(aSign, aBytes);
> -        BigInteger bNumber = new BigInteger(aSign, bBytes);
> -        int code1 = aNumber.hashCode();
> -        int code2 = bNumber.hashCode();
> -        if (!aNumber.equals(bNumber)) {
> -            assertTrue("hash codes for these unequal objects should be equal", code1 == code2);
> -        }
> -    }
> +    }      
> Test  case mentioned above:
> ----
> import java.math.BigDecimal;
> import java.text.NumberFormat;
> import java.util.Arrays;
> import java.util.HashMap;
> import java.util.Random;
> public class HashMapTest {
> 	static class MyBigDecimal extends BigDecimal {
> 		public MyBigDecimal(String s) {
> 			super(s);
> 		}
> 		static long hashCodeCallsCounter;
> 		static long equalsCallsCounter;
> 		@Override
> 		public int hashCode() {
> 			hashCodeCallsCounter ++;
> 			return super.hashCode();
> 		}
> 		
> 		@Override
> 		public boolean equals(Object arg0) {
> 			equalsCallsCounter ++;
> 			return super.equals(arg0);
> 		}
> 		
> 		static void reset() {
> 			equalsCallsCounter = 0;
> 			hashCodeCallsCounter = 0;
> 		}
> 	}
> 	
> 	
> 	static MyBigDecimal[][][] keys;
> 	static int[] hashcodes;
> 	public static HashMap createRatesMap(int dim1, int dim2,
>             int dim3, BigDecimal value1) {
>         HashMap map = new HashMap();
>         keys = new MyBigDecimal[dim1][dim2][dim3];
>         StringBuffer sb = new StringBuffer();
>         NumberFormat nf = NumberFormat.getNumberInstance();
>         nf.setMinimumIntegerDigits(3);
>         hashcodes = new int[dim1 * dim2 * dim3];
>         int ind = 0;
>         for (int i = 0; i < dim1; i ++){
>             for (int j = 0; j < dim2; j ++) {
>                 for (int k = 0; k < dim3; k ++) {
>                     long res = i * 10000 + j * 100 + k;
>                     int scale = 3 + ((i + j + k) % 5);
>                     MyBigDecimal key = new MyBigDecimal(res + "." + res);
>                     keys[i][j][k] = key;
>                     hashcodes[ind ++] = key.hashCode();
>                     map.put(key, value1.setScale(scale, BigDecimal.ROUND_DOWN));
>                 }
>             }
>         }
>         return map;
>     }	
> 	
> 	public static void main(String[] args) {
> 		HashMap map = createRatesMap(300, 10, 15, BigDecimal.ONE);
> 		Arrays.sort(hashcodes);
> 		int currentValue = 0;
> 		int currentIndex = 0;
> 		int curLength = 0;
> 		int[] value = new int[hashcodes.length];
> 		int[] counters = new int[hashcodes.length];
> 		
> 		for (int i = 0; i < hashcodes.length; i ++) {
> 			if (hashcodes[i] == currentValue) {
> 				currentIndex++;
> 				counters[curLength - 1] = currentIndex;				
> 			} else {				
> 				currentIndex = 1;
> 				currentValue = hashcodes[i];
> 				value[curLength] = currentValue;
> 				counters[curLength] = 1;
> 				curLength++;
> 			}
> 		}		
> 		
> 		MyBigDecimal.reset();		
> 		Random random = new Random();	
> 		long start = System.currentTimeMillis();
> 		for (int i = 0; i < 10000000; i ++) {
> 		    map.get(keys[random.nextInt(300)][random.nextInt(10)][random.nextInt(15)]);
> 		}
> 		System.out.println("different hash coodes: " + curLength);
> 		System.out.println("time" + (System.currentTimeMillis() - start));		
> 		
> 	}
> }

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.


[jira] Updated: (HARMONY-3086) [math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal

Posted by "Evgeniya Maenkova (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/HARMONY-3086?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Evgeniya Maenkova updated HARMONY-3086:
---------------------------------------

    Attachment: math.patch

corrected patch

> [math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal
> ---------------------------------------------------------------------
>
>                 Key: HARMONY-3086
>                 URL: https://issues.apache.org/jira/browse/HARMONY-3086
>             Project: Harmony
>          Issue Type: Improvement
>          Components: Classlib
>         Environment: Platform independent
>            Reporter: Evgeniya Maenkova
>         Assigned To: Alexey Petrenko
>         Attachments: HashMapTest.java, math.patch, math.patch
>
>
> As BigDecimal/BigInteger.hashCode isn't specified I belive we can do some improvements in this arrea.
> 1)BigInteger.hashCode computes a hash code accordingly to the latest digit only. We could take all digits into account.
> 2)BigDecimal.hashCode uses shift, so BigInteger's hashCode has pretty small influence on BigDecimal's hashCode.
>  public int hashCode() {
>         /* Take the 24 trailing bits of BigInteger hashcode
>          * and the 8 trailing bits of scale. */
>         return ((getUnscaledValue().hashCode() << 24) | (0xFF & scale));
>     }
> 3) when we are dealing with "small" BigDecimal I believe it's possible to compute hash code more effectively without redudant objects creation (I mean BigInteger).
> 4) I've written minimal test case with "big" BigDecimal as keys in some hashMap(to be attached, and see please below) and there the results (my laptop :), pretty approximately, of course):
> RI:
> --
> different hash coodes: 45000
> time6173
> --
> current java.math:
> different hash coodes: 906
> time82239
> --patched java.math:
> different hash coodes: 45000
> time6344
> (This test shows so big difference in time and hash code distribution.
> 5)I get 1 failed test with this patch:org.apache.harmony.tests.java.math.BigIntegerHashCodeTest. Test is looking doubtful as (a) hash code isn;t specified so we shouldn't require this behavior; (b) It fails on RI also. So I would propose to remove this test at all.
> -    public void testUnequalObjectsEqual() {
> -        byte aBytes[] = {56, 100, -2, -76, 98, 54, 19, 3, -15, 45, 89, -111, 69, 103, 8, -9};
> -        byte bBytes[] = {56, 100, -2, -76, 89, 45, 91, 3, -15, 45, 89, -111, 69, 103, 8, -9};
> -        int aSign = 1;
> -        BigInteger aNumber = new BigInteger(aSign, aBytes);
> -        BigInteger bNumber = new BigInteger(aSign, bBytes);
> -        int code1 = aNumber.hashCode();
> -        int code2 = bNumber.hashCode();
> -        if (!aNumber.equals(bNumber)) {
> -            assertTrue("hash codes for these unequal objects should be equal", code1 == code2);
> -        }
> -    }
> +    }      
> Test  case mentioned above:
> ----
> import java.math.BigDecimal;
> import java.text.NumberFormat;
> import java.util.Arrays;
> import java.util.HashMap;
> import java.util.Random;
> public class HashMapTest {
> 	static class MyBigDecimal extends BigDecimal {
> 		public MyBigDecimal(String s) {
> 			super(s);
> 		}
> 		static long hashCodeCallsCounter;
> 		static long equalsCallsCounter;
> 		@Override
> 		public int hashCode() {
> 			hashCodeCallsCounter ++;
> 			return super.hashCode();
> 		}
> 		
> 		@Override
> 		public boolean equals(Object arg0) {
> 			equalsCallsCounter ++;
> 			return super.equals(arg0);
> 		}
> 		
> 		static void reset() {
> 			equalsCallsCounter = 0;
> 			hashCodeCallsCounter = 0;
> 		}
> 	}
> 	
> 	
> 	static MyBigDecimal[][][] keys;
> 	static int[] hashcodes;
> 	public static HashMap createRatesMap(int dim1, int dim2,
>             int dim3, BigDecimal value1) {
>         HashMap map = new HashMap();
>         keys = new MyBigDecimal[dim1][dim2][dim3];
>         StringBuffer sb = new StringBuffer();
>         NumberFormat nf = NumberFormat.getNumberInstance();
>         nf.setMinimumIntegerDigits(3);
>         hashcodes = new int[dim1 * dim2 * dim3];
>         int ind = 0;
>         for (int i = 0; i < dim1; i ++){
>             for (int j = 0; j < dim2; j ++) {
>                 for (int k = 0; k < dim3; k ++) {
>                     long res = i * 10000 + j * 100 + k;
>                     int scale = 3 + ((i + j + k) % 5);
>                     MyBigDecimal key = new MyBigDecimal(res + "." + res);
>                     keys[i][j][k] = key;
>                     hashcodes[ind ++] = key.hashCode();
>                     map.put(key, value1.setScale(scale, BigDecimal.ROUND_DOWN));
>                 }
>             }
>         }
>         return map;
>     }	
> 	
> 	public static void main(String[] args) {
> 		HashMap map = createRatesMap(300, 10, 15, BigDecimal.ONE);
> 		Arrays.sort(hashcodes);
> 		int currentValue = 0;
> 		int currentIndex = 0;
> 		int curLength = 0;
> 		int[] value = new int[hashcodes.length];
> 		int[] counters = new int[hashcodes.length];
> 		
> 		for (int i = 0; i < hashcodes.length; i ++) {
> 			if (hashcodes[i] == currentValue) {
> 				currentIndex++;
> 				counters[curLength - 1] = currentIndex;				
> 			} else {				
> 				currentIndex = 1;
> 				currentValue = hashcodes[i];
> 				value[curLength] = currentValue;
> 				counters[curLength] = 1;
> 				curLength++;
> 			}
> 		}		
> 		
> 		MyBigDecimal.reset();		
> 		Random random = new Random();	
> 		long start = System.currentTimeMillis();
> 		for (int i = 0; i < 10000000; i ++) {
> 		    map.get(keys[random.nextInt(300)][random.nextInt(10)][random.nextInt(15)]);
> 		}
> 		System.out.println("different hash coodes: " + curLength);
> 		System.out.println("time" + (System.currentTimeMillis() - start));		
> 		
> 	}
> }

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.


[jira] Commented: (HARMONY-3086) [math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal

Posted by "Evgeniya Maenkova (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/HARMONY-3086?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#action_12469368 ] 

Evgeniya Maenkova commented on HARMONY-3086:
--------------------------------------------

License granted to ASF for inclusion in ASF works (as per the Apache Software License ยง5).


> [math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal
> ---------------------------------------------------------------------
>
>                 Key: HARMONY-3086
>                 URL: https://issues.apache.org/jira/browse/HARMONY-3086
>             Project: Harmony
>          Issue Type: Improvement
>          Components: Classlib
>         Environment: Platform independent
>            Reporter: Evgeniya Maenkova
>         Assigned To: Alexey Petrenko
>         Attachments: HashMapTest.java, math.patch, math.patch, math.patch
>
>
> As BigDecimal/BigInteger.hashCode isn't specified I belive we can do some improvements in this arrea.
> 1)BigInteger.hashCode computes a hash code accordingly to the latest digit only. We could take all digits into account.
> 2)BigDecimal.hashCode uses shift, so BigInteger's hashCode has pretty small influence on BigDecimal's hashCode.
>  public int hashCode() {
>         /* Take the 24 trailing bits of BigInteger hashcode
>          * and the 8 trailing bits of scale. */
>         return ((getUnscaledValue().hashCode() << 24) | (0xFF & scale));
>     }
> 3) when we are dealing with "small" BigDecimal I believe it's possible to compute hash code more effectively without redudant objects creation (I mean BigInteger).
> 4) I've written minimal test case with "big" BigDecimal as keys in some hashMap(to be attached, and see please below) and there the results (my laptop :), pretty approximately, of course):
> RI:
> --
> different hash coodes: 45000
> time6173
> --
> current java.math:
> different hash coodes: 906
> time82239
> --patched java.math:
> different hash coodes: 45000
> time6344
> (This test shows so big difference in time and hash code distribution.
> 5)I get 1 failed test with this patch:org.apache.harmony.tests.java.math.BigIntegerHashCodeTest. Test is looking doubtful as (a) hash code isn;t specified so we shouldn't require this behavior; (b) It fails on RI also. So I would propose to remove this test at all.
> -    public void testUnequalObjectsEqual() {
> -        byte aBytes[] = {56, 100, -2, -76, 98, 54, 19, 3, -15, 45, 89, -111, 69, 103, 8, -9};
> -        byte bBytes[] = {56, 100, -2, -76, 89, 45, 91, 3, -15, 45, 89, -111, 69, 103, 8, -9};
> -        int aSign = 1;
> -        BigInteger aNumber = new BigInteger(aSign, aBytes);
> -        BigInteger bNumber = new BigInteger(aSign, bBytes);
> -        int code1 = aNumber.hashCode();
> -        int code2 = bNumber.hashCode();
> -        if (!aNumber.equals(bNumber)) {
> -            assertTrue("hash codes for these unequal objects should be equal", code1 == code2);
> -        }
> -    }
> +    }      
> Test  case mentioned above:
> ----
> import java.math.BigDecimal;
> import java.text.NumberFormat;
> import java.util.Arrays;
> import java.util.HashMap;
> import java.util.Random;
> public class HashMapTest {
> 	static class MyBigDecimal extends BigDecimal {
> 		public MyBigDecimal(String s) {
> 			super(s);
> 		}
> 		static long hashCodeCallsCounter;
> 		static long equalsCallsCounter;
> 		@Override
> 		public int hashCode() {
> 			hashCodeCallsCounter ++;
> 			return super.hashCode();
> 		}
> 		
> 		@Override
> 		public boolean equals(Object arg0) {
> 			equalsCallsCounter ++;
> 			return super.equals(arg0);
> 		}
> 		
> 		static void reset() {
> 			equalsCallsCounter = 0;
> 			hashCodeCallsCounter = 0;
> 		}
> 	}
> 	
> 	
> 	static MyBigDecimal[][][] keys;
> 	static int[] hashcodes;
> 	public static HashMap createRatesMap(int dim1, int dim2,
>             int dim3, BigDecimal value1) {
>         HashMap map = new HashMap();
>         keys = new MyBigDecimal[dim1][dim2][dim3];
>         StringBuffer sb = new StringBuffer();
>         NumberFormat nf = NumberFormat.getNumberInstance();
>         nf.setMinimumIntegerDigits(3);
>         hashcodes = new int[dim1 * dim2 * dim3];
>         int ind = 0;
>         for (int i = 0; i < dim1; i ++){
>             for (int j = 0; j < dim2; j ++) {
>                 for (int k = 0; k < dim3; k ++) {
>                     long res = i * 10000 + j * 100 + k;
>                     int scale = 3 + ((i + j + k) % 5);
>                     MyBigDecimal key = new MyBigDecimal(res + "." + res);
>                     keys[i][j][k] = key;
>                     hashcodes[ind ++] = key.hashCode();
>                     map.put(key, value1.setScale(scale, BigDecimal.ROUND_DOWN));
>                 }
>             }
>         }
>         return map;
>     }	
> 	
> 	public static void main(String[] args) {
> 		HashMap map = createRatesMap(300, 10, 15, BigDecimal.ONE);
> 		Arrays.sort(hashcodes);
> 		int currentValue = 0;
> 		int currentIndex = 0;
> 		int curLength = 0;
> 		int[] value = new int[hashcodes.length];
> 		int[] counters = new int[hashcodes.length];
> 		
> 		for (int i = 0; i < hashcodes.length; i ++) {
> 			if (hashcodes[i] == currentValue) {
> 				currentIndex++;
> 				counters[curLength - 1] = currentIndex;				
> 			} else {				
> 				currentIndex = 1;
> 				currentValue = hashcodes[i];
> 				value[curLength] = currentValue;
> 				counters[curLength] = 1;
> 				curLength++;
> 			}
> 		}		
> 		
> 		MyBigDecimal.reset();		
> 		Random random = new Random();	
> 		long start = System.currentTimeMillis();
> 		for (int i = 0; i < 10000000; i ++) {
> 		    map.get(keys[random.nextInt(300)][random.nextInt(10)][random.nextInt(15)]);
> 		}
> 		System.out.println("different hash coodes: " + curLength);
> 		System.out.println("time" + (System.currentTimeMillis() - start));		
> 		
> 	}
> }

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.


[jira] Updated: (HARMONY-3086) [math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal

Posted by "Evgeniya Maenkova (JIRA)" <ji...@apache.org>.
     [ https://issues.apache.org/jira/browse/HARMONY-3086?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Evgeniya Maenkova updated HARMONY-3086:
---------------------------------------

    Attachment: math.patch

test (forgot to remove org.apache.harmony.tests.java.math.BigIntegerHashCodeTest in previous patch)

> [math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal
> ---------------------------------------------------------------------
>
>                 Key: HARMONY-3086
>                 URL: https://issues.apache.org/jira/browse/HARMONY-3086
>             Project: Harmony
>          Issue Type: Improvement
>          Components: Classlib
>         Environment: Platform independent
>            Reporter: Evgeniya Maenkova
>         Assigned To: Alexey Petrenko
>         Attachments: HashMapTest.java, math.patch, math.patch, math.patch
>
>
> As BigDecimal/BigInteger.hashCode isn't specified I belive we can do some improvements in this arrea.
> 1)BigInteger.hashCode computes a hash code accordingly to the latest digit only. We could take all digits into account.
> 2)BigDecimal.hashCode uses shift, so BigInteger's hashCode has pretty small influence on BigDecimal's hashCode.
>  public int hashCode() {
>         /* Take the 24 trailing bits of BigInteger hashcode
>          * and the 8 trailing bits of scale. */
>         return ((getUnscaledValue().hashCode() << 24) | (0xFF & scale));
>     }
> 3) when we are dealing with "small" BigDecimal I believe it's possible to compute hash code more effectively without redudant objects creation (I mean BigInteger).
> 4) I've written minimal test case with "big" BigDecimal as keys in some hashMap(to be attached, and see please below) and there the results (my laptop :), pretty approximately, of course):
> RI:
> --
> different hash coodes: 45000
> time6173
> --
> current java.math:
> different hash coodes: 906
> time82239
> --patched java.math:
> different hash coodes: 45000
> time6344
> (This test shows so big difference in time and hash code distribution.
> 5)I get 1 failed test with this patch:org.apache.harmony.tests.java.math.BigIntegerHashCodeTest. Test is looking doubtful as (a) hash code isn;t specified so we shouldn't require this behavior; (b) It fails on RI also. So I would propose to remove this test at all.
> -    public void testUnequalObjectsEqual() {
> -        byte aBytes[] = {56, 100, -2, -76, 98, 54, 19, 3, -15, 45, 89, -111, 69, 103, 8, -9};
> -        byte bBytes[] = {56, 100, -2, -76, 89, 45, 91, 3, -15, 45, 89, -111, 69, 103, 8, -9};
> -        int aSign = 1;
> -        BigInteger aNumber = new BigInteger(aSign, aBytes);
> -        BigInteger bNumber = new BigInteger(aSign, bBytes);
> -        int code1 = aNumber.hashCode();
> -        int code2 = bNumber.hashCode();
> -        if (!aNumber.equals(bNumber)) {
> -            assertTrue("hash codes for these unequal objects should be equal", code1 == code2);
> -        }
> -    }
> +    }      
> Test  case mentioned above:
> ----
> import java.math.BigDecimal;
> import java.text.NumberFormat;
> import java.util.Arrays;
> import java.util.HashMap;
> import java.util.Random;
> public class HashMapTest {
> 	static class MyBigDecimal extends BigDecimal {
> 		public MyBigDecimal(String s) {
> 			super(s);
> 		}
> 		static long hashCodeCallsCounter;
> 		static long equalsCallsCounter;
> 		@Override
> 		public int hashCode() {
> 			hashCodeCallsCounter ++;
> 			return super.hashCode();
> 		}
> 		
> 		@Override
> 		public boolean equals(Object arg0) {
> 			equalsCallsCounter ++;
> 			return super.equals(arg0);
> 		}
> 		
> 		static void reset() {
> 			equalsCallsCounter = 0;
> 			hashCodeCallsCounter = 0;
> 		}
> 	}
> 	
> 	
> 	static MyBigDecimal[][][] keys;
> 	static int[] hashcodes;
> 	public static HashMap createRatesMap(int dim1, int dim2,
>             int dim3, BigDecimal value1) {
>         HashMap map = new HashMap();
>         keys = new MyBigDecimal[dim1][dim2][dim3];
>         StringBuffer sb = new StringBuffer();
>         NumberFormat nf = NumberFormat.getNumberInstance();
>         nf.setMinimumIntegerDigits(3);
>         hashcodes = new int[dim1 * dim2 * dim3];
>         int ind = 0;
>         for (int i = 0; i < dim1; i ++){
>             for (int j = 0; j < dim2; j ++) {
>                 for (int k = 0; k < dim3; k ++) {
>                     long res = i * 10000 + j * 100 + k;
>                     int scale = 3 + ((i + j + k) % 5);
>                     MyBigDecimal key = new MyBigDecimal(res + "." + res);
>                     keys[i][j][k] = key;
>                     hashcodes[ind ++] = key.hashCode();
>                     map.put(key, value1.setScale(scale, BigDecimal.ROUND_DOWN));
>                 }
>             }
>         }
>         return map;
>     }	
> 	
> 	public static void main(String[] args) {
> 		HashMap map = createRatesMap(300, 10, 15, BigDecimal.ONE);
> 		Arrays.sort(hashcodes);
> 		int currentValue = 0;
> 		int currentIndex = 0;
> 		int curLength = 0;
> 		int[] value = new int[hashcodes.length];
> 		int[] counters = new int[hashcodes.length];
> 		
> 		for (int i = 0; i < hashcodes.length; i ++) {
> 			if (hashcodes[i] == currentValue) {
> 				currentIndex++;
> 				counters[curLength - 1] = currentIndex;				
> 			} else {				
> 				currentIndex = 1;
> 				currentValue = hashcodes[i];
> 				value[curLength] = currentValue;
> 				counters[curLength] = 1;
> 				curLength++;
> 			}
> 		}		
> 		
> 		MyBigDecimal.reset();		
> 		Random random = new Random();	
> 		long start = System.currentTimeMillis();
> 		for (int i = 0; i < 10000000; i ++) {
> 		    map.get(keys[random.nextInt(300)][random.nextInt(10)][random.nextInt(15)]);
> 		}
> 		System.out.println("different hash coodes: " + curLength);
> 		System.out.println("time" + (System.currentTimeMillis() - start));		
> 		
> 	}
> }

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.


[jira] Commented: (HARMONY-3086) [math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal

Posted by "Evgeniya Maenkova (JIRA)" <ji...@apache.org>.
    [ https://issues.apache.org/jira/browse/HARMONY-3086?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#action_12468681 ] 

Evgeniya Maenkova commented on HARMONY-3086:
--------------------------------------------

Alexey, thanks for finding this. Mistake in my patch. I belive there will be no influence on performance:
old patch's BigInteger.hashCode:
public int hashCode() {
    	if (hashCode != 0) {
    	    return hashCode;	
    	}    	  
    	System.out.println(digits.length);
    	for (int i = 0; i < digits.length; i ++) {
    		hashCode = (int)(hashCode * 33 + (digits[i] & 0xffffffff));    		
    	}  
        return hashCode * sign;        
    }
new one:
public int hashCode() {
    	if (hashCode != 0) {
    	    return hashCode;	
    	}    	  
    	System.out.println(digits.length);
    	for (int i = 0; i < digits.length; i ++) {
    		hashCode = (int)(hashCode * 33 + (digits[i] & 0xffffffff));    		
    	}  
	hashCode = hashCode * sign;
        return hashCode;        
    }

I'll attach this patch right now.

> [math] poor hashCode in java.math.BigDecimal and java.math.BigDecimal
> ---------------------------------------------------------------------
>
>                 Key: HARMONY-3086
>                 URL: https://issues.apache.org/jira/browse/HARMONY-3086
>             Project: Harmony
>          Issue Type: Improvement
>          Components: Classlib
>         Environment: Platform independent
>            Reporter: Evgeniya Maenkova
>         Assigned To: Alexey Petrenko
>         Attachments: HashMapTest.java, math.patch, math.patch
>
>
> As BigDecimal/BigInteger.hashCode isn't specified I belive we can do some improvements in this arrea.
> 1)BigInteger.hashCode computes a hash code accordingly to the latest digit only. We could take all digits into account.
> 2)BigDecimal.hashCode uses shift, so BigInteger's hashCode has pretty small influence on BigDecimal's hashCode.
>  public int hashCode() {
>         /* Take the 24 trailing bits of BigInteger hashcode
>          * and the 8 trailing bits of scale. */
>         return ((getUnscaledValue().hashCode() << 24) | (0xFF & scale));
>     }
> 3) when we are dealing with "small" BigDecimal I believe it's possible to compute hash code more effectively without redudant objects creation (I mean BigInteger).
> 4) I've written minimal test case with "big" BigDecimal as keys in some hashMap(to be attached, and see please below) and there the results (my laptop :), pretty approximately, of course):
> RI:
> --
> different hash coodes: 45000
> time6173
> --
> current java.math:
> different hash coodes: 906
> time82239
> --patched java.math:
> different hash coodes: 45000
> time6344
> (This test shows so big difference in time and hash code distribution.
> 5)I get 1 failed test with this patch:org.apache.harmony.tests.java.math.BigIntegerHashCodeTest. Test is looking doubtful as (a) hash code isn;t specified so we shouldn't require this behavior; (b) It fails on RI also. So I would propose to remove this test at all.
> -    public void testUnequalObjectsEqual() {
> -        byte aBytes[] = {56, 100, -2, -76, 98, 54, 19, 3, -15, 45, 89, -111, 69, 103, 8, -9};
> -        byte bBytes[] = {56, 100, -2, -76, 89, 45, 91, 3, -15, 45, 89, -111, 69, 103, 8, -9};
> -        int aSign = 1;
> -        BigInteger aNumber = new BigInteger(aSign, aBytes);
> -        BigInteger bNumber = new BigInteger(aSign, bBytes);
> -        int code1 = aNumber.hashCode();
> -        int code2 = bNumber.hashCode();
> -        if (!aNumber.equals(bNumber)) {
> -            assertTrue("hash codes for these unequal objects should be equal", code1 == code2);
> -        }
> -    }
> +    }      
> Test  case mentioned above:
> ----
> import java.math.BigDecimal;
> import java.text.NumberFormat;
> import java.util.Arrays;
> import java.util.HashMap;
> import java.util.Random;
> public class HashMapTest {
> 	static class MyBigDecimal extends BigDecimal {
> 		public MyBigDecimal(String s) {
> 			super(s);
> 		}
> 		static long hashCodeCallsCounter;
> 		static long equalsCallsCounter;
> 		@Override
> 		public int hashCode() {
> 			hashCodeCallsCounter ++;
> 			return super.hashCode();
> 		}
> 		
> 		@Override
> 		public boolean equals(Object arg0) {
> 			equalsCallsCounter ++;
> 			return super.equals(arg0);
> 		}
> 		
> 		static void reset() {
> 			equalsCallsCounter = 0;
> 			hashCodeCallsCounter = 0;
> 		}
> 	}
> 	
> 	
> 	static MyBigDecimal[][][] keys;
> 	static int[] hashcodes;
> 	public static HashMap createRatesMap(int dim1, int dim2,
>             int dim3, BigDecimal value1) {
>         HashMap map = new HashMap();
>         keys = new MyBigDecimal[dim1][dim2][dim3];
>         StringBuffer sb = new StringBuffer();
>         NumberFormat nf = NumberFormat.getNumberInstance();
>         nf.setMinimumIntegerDigits(3);
>         hashcodes = new int[dim1 * dim2 * dim3];
>         int ind = 0;
>         for (int i = 0; i < dim1; i ++){
>             for (int j = 0; j < dim2; j ++) {
>                 for (int k = 0; k < dim3; k ++) {
>                     long res = i * 10000 + j * 100 + k;
>                     int scale = 3 + ((i + j + k) % 5);
>                     MyBigDecimal key = new MyBigDecimal(res + "." + res);
>                     keys[i][j][k] = key;
>                     hashcodes[ind ++] = key.hashCode();
>                     map.put(key, value1.setScale(scale, BigDecimal.ROUND_DOWN));
>                 }
>             }
>         }
>         return map;
>     }	
> 	
> 	public static void main(String[] args) {
> 		HashMap map = createRatesMap(300, 10, 15, BigDecimal.ONE);
> 		Arrays.sort(hashcodes);
> 		int currentValue = 0;
> 		int currentIndex = 0;
> 		int curLength = 0;
> 		int[] value = new int[hashcodes.length];
> 		int[] counters = new int[hashcodes.length];
> 		
> 		for (int i = 0; i < hashcodes.length; i ++) {
> 			if (hashcodes[i] == currentValue) {
> 				currentIndex++;
> 				counters[curLength - 1] = currentIndex;				
> 			} else {				
> 				currentIndex = 1;
> 				currentValue = hashcodes[i];
> 				value[curLength] = currentValue;
> 				counters[curLength] = 1;
> 				curLength++;
> 			}
> 		}		
> 		
> 		MyBigDecimal.reset();		
> 		Random random = new Random();	
> 		long start = System.currentTimeMillis();
> 		for (int i = 0; i < 10000000; i ++) {
> 		    map.get(keys[random.nextInt(300)][random.nextInt(10)][random.nextInt(15)]);
> 		}
> 		System.out.println("different hash coodes: " + curLength);
> 		System.out.println("time" + (System.currentTimeMillis() - start));		
> 		
> 	}
> }

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.