Skip to content

Using ThreadLocal Digester for NameBasedGenerator to avoid synchronized #88

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions src/main/java/com/fasterxml/uuid/Generators.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<MessageDigest> threadLocalDisgester = new ThreadLocal<MessageDigest>() {
@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);
}
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/com/fasterxml/uuid/StringArgGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,25 @@ 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.
Comment on lines +19 to +22

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: It will be better if we can provide an use-case example when we should use this method.

*/
public abstract UUID concurrentGenerate(String name);
/**
* Method for generating name-based UUIDs using specified byte-serialization
* of name.
*
* @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.
Comment on lines +33 to +36

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above, can we put an example when to use this method

*/
public abstract UUID concurrentGenerate(byte[] nameBytes);

}
20 changes: 19 additions & 1 deletion src/main/java/com/fasterxml/uuid/impl/NameBasedGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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);
}
}
94 changes: 87 additions & 7 deletions src/main/java/perf/MeasurePerformance.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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");
}
Expand Down Expand Up @@ -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");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "SHA-1" can be declared as a constant

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();
Expand Down