From 972c28e8c8bd7d43f5a0cf975bef372746e1fcc9 Mon Sep 17 00:00:00 2001 From: Magno Yu Date: Fri, 20 Oct 2023 10:23:23 -0400 Subject: [PATCH] Add concurrent generate to name based generator if digester is created with ThreadLocal --- .../java/com/fasterxml/uuid/Generators.java | 18 ++-- .../fasterxml/uuid/StringArgGenerator.java | 14 +++ .../uuid/impl/NameBasedGenerator.java | 20 +++- src/main/java/perf/MeasurePerformance.java | 94 +++++++++++++++++-- 4 files changed, 132 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/fasterxml/uuid/Generators.java b/src/main/java/com/fasterxml/uuid/Generators.java index 1c4fe6b..bfe726c 100644 --- a/src/main/java/com/fasterxml/uuid/Generators.java +++ b/src/main/java/com/fasterxml/uuid/Generators.java @@ -110,12 +110,18 @@ public static NameBasedGenerator nameBasedGenerator(UUID namespace, MessageDiges { UUIDType type = null; if (digester == null) { - try { - digester = MessageDigest.getInstance("SHA-1"); - type = UUIDType.NAME_BASED_SHA1; - } catch (NoSuchAlgorithmException nex) { - throw new IllegalArgumentException("Couldn't instantiate SHA-1 MessageDigest instance: "+nex.toString()); - } + ThreadLocal threadLocalDisgester = new ThreadLocal() { + @Override + protected MessageDigest initialValue() { + try { + return MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException nex) { + throw new IllegalArgumentException("Couldn't instantiate SHA-1 MessageDigest instance: "+ nex.toString()); + } + } + }; + digester = threadLocalDisgester.get(); + type = UUIDType.NAME_BASED_SHA1; } return new NameBasedGenerator(namespace, digester, type); } diff --git a/src/main/java/com/fasterxml/uuid/StringArgGenerator.java b/src/main/java/com/fasterxml/uuid/StringArgGenerator.java index 1bf4925..12f6e6f 100644 --- a/src/main/java/com/fasterxml/uuid/StringArgGenerator.java +++ b/src/main/java/com/fasterxml/uuid/StringArgGenerator.java @@ -16,6 +16,12 @@ public abstract class StringArgGenerator extends UUIDGenerator */ public abstract UUID generate(String name); + /** + * Method for generating name-based UUIDs using specified name (serialized to + * bytes using UTF-8 encoding). No synchronization is performed on digester. + * Digester is assumed to be created with ThreadLocal. + */ + public abstract UUID concurrentGenerate(String name); /** * Method for generating name-based UUIDs using specified byte-serialization * of name. @@ -23,4 +29,12 @@ public abstract class StringArgGenerator extends UUIDGenerator * @since 3.1 */ public abstract UUID generate(byte[] nameBytes); + + /** + * Method for generating name-based UUIDs using specified byte-serialization + * of name. No synchronization is performed on digester. Digester is assumed + * to be created with ThreadLocal. + */ + public abstract UUID concurrentGenerate(byte[] nameBytes); + } diff --git a/src/main/java/com/fasterxml/uuid/impl/NameBasedGenerator.java b/src/main/java/com/fasterxml/uuid/impl/NameBasedGenerator.java index 9e76a56..64ff1cb 100644 --- a/src/main/java/com/fasterxml/uuid/impl/NameBasedGenerator.java +++ b/src/main/java/com/fasterxml/uuid/impl/NameBasedGenerator.java @@ -121,7 +121,13 @@ public UUID generate(String name) // !!! TODO: 14-Oct-2010, tatu: can repurpose faster UTF-8 encoding from Jackson return generate(name.getBytes(_utf8)); } - + + @Override + public UUID concurrentGenerate(String name) + { + return concurrentGenerate(name.getBytes(_utf8)); + } + @Override public UUID generate(byte[] nameBytes) { @@ -136,4 +142,16 @@ public UUID generate(byte[] nameBytes) } return UUIDUtil.constructUUID(_type, digest); } + + @Override + public UUID concurrentGenerate(byte[] nameBytes){ + byte[] digest; + _digester.reset(); + if (_namespace != null) { + _digester.update(UUIDUtil.asByteArray(_namespace)); + } + _digester.update(nameBytes); + digest = _digester.digest(); + return UUIDUtil.constructUUID(_type, digest); + } } diff --git a/src/main/java/perf/MeasurePerformance.java b/src/main/java/perf/MeasurePerformance.java index 570bc41..a2976de 100644 --- a/src/main/java/perf/MeasurePerformance.java +++ b/src/main/java/perf/MeasurePerformance.java @@ -1,6 +1,10 @@ package perf; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.UUID; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; import com.fasterxml.uuid.*; import com.fasterxml.uuid.impl.RandomBasedGenerator; @@ -51,11 +55,14 @@ public void test() throws Exception final TimeBasedGenerator timeGenPlain = Generators.timeBasedGenerator(nic); final TimeBasedGenerator timeGenSynced = Generators.timeBasedGenerator(nic, new com.fasterxml.uuid.ext.FileBasedTimestampSynchronizer()); - final StringArgGenerator nameGen = Generators.nameBasedGenerator(namespaceForNamed); - + + final MessageDigest digester = MessageDigest.getInstance("SHA-1"); + final StringArgGenerator nameGen = Generators.nameBasedGenerator(namespaceForNamed, digester); + final StringArgGenerator nameGenConcurrent = Generators.nameBasedGenerator(namespaceForNamed); + while (true) { try { Thread.sleep(100L); } catch (InterruptedException ie) { } - int round = (i++ % 7); + int round = (i++ % 10); long curr = System.currentTimeMillis(); String msg; @@ -93,19 +100,32 @@ public void test() throws Exception testRandom(uuids, ROUNDS, utilRandomGen); break; - case 6: msg = "Jug, name-based"; testNameBased(uuids, ROUNDS, nameGen); break; - /* case 7: + msg = "Jug, name-based, concurrent"; + testNameBasedConcurrent(uuids, ROUNDS, nameGenConcurrent); + break; + + case 8: + msg = "Jug, name-based, ten threads"; + testNameBasedTenThreads(uuids, ROUNDS, namespaceForNamed); + break; + + case 9: + msg = "Jug, name-based, concurrent, ten threads"; + testNameBasedConcurrentTenThreads(uuids, ROUNDS, namespaceForNamed); + break; + /* + case 8: msg = "http://johannburkard.de/software/uuid/"; testUUID32(uuids, ROUNDS); break; */ - + default: throw new Error("Internal error"); } @@ -175,7 +195,67 @@ private final void testNameBased(Object[] uuids, int rounds, StringArgGenerator } } } - + + private final void testNameBasedConcurrent(Object[] uuids, int rounds, StringArgGenerator uuidGen) + { + while (--rounds >= 0) { + for (int i = 0, len = uuids.length; i < len; ++i) { + uuids[i] = uuidGen.concurrentGenerate(NAME); + } + } + } + + private final void testNameBasedTenThreads(Object[] uuids, int rounds, final UUID namespaceForNamed) throws InterruptedException, BrokenBarrierException, NoSuchAlgorithmException + { + while (--rounds >= 0) { + final CyclicBarrier gate = new CyclicBarrier(11); + final MessageDigest digester = MessageDigest.getInstance("SHA-1"); + final StringArgGenerator nameGen = Generators.nameBasedGenerator(namespaceForNamed, digester); + for (int j = 0; j < 10; j ++) { + final Object[] fuuids = uuids; + Thread t = new Thread(new Runnable() { + public void run() { + try { + gate.await(); + for (int i = 0, len = fuuids.length; i < len; ++i) { + fuuids[i] = nameGen.generate(NAME); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + t.start(); + } + gate.await(); + } + } + + private final void testNameBasedConcurrentTenThreads(Object[] uuids, int rounds, final UUID namespaceForNamed) throws InterruptedException, BrokenBarrierException, NoSuchAlgorithmException + { + while (--rounds >= 0) { + final CyclicBarrier gate = new CyclicBarrier(11); + final Object[] fuuids = uuids; + for (int j = 0; j < 10; j ++) { + Thread t = new Thread(new Runnable() { + public void run() { + final StringArgGenerator nameGen = Generators.nameBasedGenerator(namespaceForNamed); + try { + gate.await(); + for (int i = 0, len = fuuids.length; i < len; ++i) { + fuuids[i] = nameGen.concurrentGenerate(NAME); + } + } catch (Exception e){ + e.printStackTrace(); + } + } + }); + t.start(); + } + gate.await(); + } + } + public static void main(String[] args) throws Exception { new MeasurePerformance().test();