Browse Source

HADOOP-3556. Removed lock contention in MD5Hash by changing the
singleton MessageDigester by an instance per Thread using
ThreadLocal. Contributed by Iv‡n de Prado.


git-svn-id: https://svn.apache.org/repos/asf/hadoop/core/trunk@672823 13f79535-47bb-0310-9956-ffa450edef68

Owen O'Malley 17 years ago
parent
commit
bac1003136

+ 5 - 0
CHANGES.txt

@@ -30,6 +30,7 @@ Trunk (unreleased changes)
     All of them default to "\t". (Zheng Shao via omalley)
 
   IMPROVEMENTS
+
     HADOOP-3577. Tools to inject blocks into name node and simulated
     data nodes for testing. (Sanjay Radia via hairong)
 
@@ -38,6 +39,10 @@ Trunk (unreleased changes)
 
   OPTIMIZATIONS
 
+    HADOOP-3556. Removed lock contention in MD5Hash by changing the 
+    singleton MessageDigester by an instance per Thread using 
+    ThreadLocal. (Iv‡n de Prado via omalley)
+
   BUG FIXES
 
     HADOOP-3563.  Refactor the distributed upgrade code so that it is 

+ 11 - 11
src/core/org/apache/hadoop/io/MD5Hash.java

@@ -28,14 +28,15 @@ import java.security.*;
  */
 public class MD5Hash implements WritableComparable {
   public static final int MD5_LEN = 16;
-  private static final MessageDigest DIGESTER;
-  static {
-    try {
-      DIGESTER = MessageDigest.getInstance("MD5");
-    } catch (NoSuchAlgorithmException e) {
-      throw new RuntimeException(e);
+  private static ThreadLocal<MessageDigest> DIGESTER_FACTORY = new ThreadLocal<MessageDigest>() {
+    protected MessageDigest initialValue() {
+      try {
+        return MessageDigest.getInstance("MD5");
+      } catch (NoSuchAlgorithmException e) {
+        throw new RuntimeException(e);
+      }
     }
-  }
+  };
 
   private byte[] digest;
 
@@ -89,10 +90,9 @@ public class MD5Hash implements WritableComparable {
   /** Construct a hash value for a byte array. */
   public static MD5Hash digest(byte[] data, int start, int len) {
     byte[] digest;
-    synchronized (DIGESTER) {
-      DIGESTER.update(data, start, len);
-      digest = DIGESTER.digest();
-    }
+    MessageDigest digester = DIGESTER_FACTORY.get();
+    digester.update(data, start, len);
+    digest = digester.digest();
     return new MD5Hash(digest);
   }
 

+ 31 - 5
src/test/org/apache/hadoop/io/TestMD5Hash.java

@@ -37,15 +37,17 @@ public class TestMD5Hash extends TestCase {
     return new MD5Hash(digest.digest());
   }
 
+  protected static byte[] D00 = new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+  protected static byte[] DFF = new byte[] {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; 
+  
   public void testMD5Hash() throws Exception {
     MD5Hash md5Hash = getTestHash();
 
-    MD5Hash md5Hash00
-      = new MD5Hash(new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
+    final MD5Hash md5Hash00
+      = new MD5Hash(D00);
 
-    MD5Hash md5HashFF
-      = new MD5Hash(new byte[] {-1, -1, -1, -1, -1, -1, -1, -1,
-                                -1, -1, -1, -1, -1, -1, -1, -1});
+    final MD5Hash md5HashFF
+      = new MD5Hash(DFF);
     
     MD5Hash orderedHash = new MD5Hash(new byte[]{1,2,3,4,5,6,7,8,9,10,11,12,
                                                  13,14,15,16});
@@ -84,6 +86,30 @@ public class TestMD5Hash extends TestCase {
     assertEquals(0xfffefdfcfbfaf9f8L, backwardHash.halfDigest());
     assertTrue("hash collision", 
                closeHash1.hashCode() != closeHash2.hashCode());
+     
+    Thread t1 = new Thread() {      
+      public void run() {
+        for (int i = 0; i < 100; i++) {
+          MD5Hash hash = new MD5Hash(DFF);
+          assertEquals(hash, md5HashFF);
+        }        
+      }
+    };
+    
+    Thread t2 = new Thread() {
+      public void run() {
+        for (int i = 0; i < 100; i++) {
+          MD5Hash hash = new MD5Hash(D00);
+          assertEquals(hash, md5Hash00);
+        }
+      }      
+    };
+    
+    t1.start();
+    t2.start();
+    t1.join();
+    t2.join();
+    
   }
 	
 }