Skip to content

Instantly share code, notes, and snippets.

@cburroughs
Last active March 20, 2017 17:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cburroughs/85b452c132014fe0e2c725901f2f994f to your computer and use it in GitHub Desktop.
Save cburroughs/85b452c132014fe0e2c725901f2f994f to your computer and use it in GitHub Desktop.
/*
* Copyright (c) 2017, Joyent, Inc. All rights reserved.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.joyent.http.signature;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import java.io.IOException;
import java.security.KeyPair;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.StampedLock;
import java.util.concurrent.locks.ReentrantLock;
@State(Scope.Benchmark)
public class BenchmarkTemp {
// simple trylock
// stamped
public static abstract class SignCache {
public abstract String createAuthorizationHeader(Signer signer, final String login,
final String fingerprint,
final KeyPair keyPair,
final String date);
}
// Unbounded wrongness
public static class SloppySignCache extends SignCache {
protected String lastDate = null;
protected String lastSign = null;
@Override
public String createAuthorizationHeader(Signer signer, final String login,
final String fingerprint,
final KeyPair keyPair,
final String date) {
if (date.equals(lastDate)) {
return lastSign;
} else {
// try {
// TimeUnit.SECONDS.sleep(1);
// } catch (Exception e) {}
lastDate = date;
lastSign = signer.createAuthorizationHeader(login, fingerprint, keyPair, date);
return lastSign;
}
}
}
public static class SyncSignCache extends SloppySignCache {
@Override
public String createAuthorizationHeader(Signer signer, final String login,
final String fingerprint,
final KeyPair keyPair,
final String date) {
synchronized(this) {
return super.createAuthorizationHeader(signer, login, fingerprint, keyPair, date);
}
}
}
public static class TryLockSignCache extends SignCache {
protected String lastDate = null;
protected String lastSign = null;
private final ReentrantLock lock = new ReentrantLock();;
@Override
public String createAuthorizationHeader(Signer signer, final String login,
final String fingerprint,
final KeyPair keyPair,
final String date) {
try {
if (lock.tryLock(500, TimeUnit.MICROSECONDS)) {
if (date.equals(lastDate)) {
return lastSign;
} else {
lastDate = date;
lastSign = signer.createAuthorizationHeader(login, fingerprint, keyPair, date);
return lastSign;
}
}
} catch (InterruptedException ignored) {}
return signer.createAuthorizationHeader(login, fingerprint, keyPair, date);
}
}
public static class RWSignCache extends SignCache {
protected String lastDate = null;
protected String lastSign = null;
private final ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock();
@Override
public String createAuthorizationHeader(Signer signer, final String login,
final String fingerprint,
final KeyPair keyPair,
final String date) {
rwlock.readLock().lock();
try {
if (date.equals(lastDate)) {
return lastSign;
} else {
rwlock.readLock().unlock();
rwlock.writeLock().lock();
try {
// Downgrade for consistent unlock in finally
rwlock.readLock().lock();
// Double check after lock shuffle
if (date.equals(lastDate)) {
return lastSign;
}
lastDate = date;
lastSign = signer.createAuthorizationHeader(login, fingerprint, keyPair, date);
return lastSign;
} finally {
rwlock.writeLock().unlock();
}
}
} finally {
rwlock.readLock().unlock();
}
}
}
public static class StampedSignCache extends SignCache {
protected String lastDate = null;
protected String lastSign = null;
private final StampedLock slock = new StampedLock();
@Override
public String createAuthorizationHeader(Signer signer, final String login,
final String fingerprint,
final KeyPair keyPair,
final String date) {
long stamp = slock.tryOptimisticRead();
if (date.equals(lastDate)) {
String ret = lastSign;
if (slock.validate(stamp)) {
return ret;
} else {
return reCheckAndWriteIfNeeded(signer, login, fingerprint, keyPair, date);
}
} else {
if (slock.validate(stamp)) {
long ws = slock.tryConvertToWriteLock(stamp);
if (ws == 0) {
//sl.unlockRead(stamp); //??
stamp = slock.writeLock();
} else {
stamp = ws;
}
try {
lastDate = date;
lastSign = signer.createAuthorizationHeader(login, fingerprint, keyPair, date);
return lastSign;
} finally {
slock.unlockWrite(stamp);
}
} else {
return reCheckAndWriteIfNeeded(signer, login, fingerprint, keyPair, date);
}
}
}
private String reCheckAndWriteIfNeeded(final Signer signer, final String login,
final String fingerprint,
final KeyPair keyPair,
final String date) {
long stamp = slock.readLock();
try {
while (!date.equals(lastDate)) {
long ws = slock.tryConvertToWriteLock(stamp);
if (ws != 0L) {
stamp = ws;
lastDate = date;
lastSign = signer.createAuthorizationHeader(login, fingerprint, keyPair, date);
return lastSign;
}
else {
slock.unlockRead(stamp);
stamp = slock.writeLock();
}
}
return lastSign;
} finally {
slock.unlock(stamp);
}
}
}
protected KeyPair keyPair;
protected String testKeyFingerprint;
protected ThreadLocalSigner signer;
private boolean firstSetup = true;
private SignCache signCache;
@Param({"SHA1", "SHA256", "SHA512"})
private String hash;
@Param({"stdlib", "native.jnagmp"})
private String providerCode;
@Param({"sloppy", "sync", "trylock", "rwlock", "stamped"})
private String cacheType;
public String getKeyCode() {
return "rsa_2048";
}
@Setup
public void setup() throws IOException {
testKeyFingerprint = SignerTestUtil.testKeyFingerprint(getKeyCode());
keyPair = SignerTestUtil.testKeyPair(getKeyCode());
signer = new ThreadLocalSigner(new Signer.Builder(keyPair).hash(hash).providerCode(providerCode));
if (cacheType.equals("sloppy")) {
signCache = new SloppySignCache();
} else if (cacheType.equals("sync")) {
signCache = new SyncSignCache();
} else if (cacheType.equals("trylock")) {
signCache = new TryLockSignCache();
} else if (cacheType.equals("rwlock")) {
signCache = new RWSignCache();
} else if(cacheType.equals("stamped")) {
signCache = new StampedSignCache();
}
if (firstSetup) {
System.out.println("\n#Signature-->Provider: " + signer.get().getSignature().getProvider().getName());
firstSetup = false;
}
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
public String signHeaderThroughput() {
String now = signer.get().defaultSignDateAsString();
return signHeader(now);
}
@Benchmark
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public String signHeaderLatency() {
String now = signer.get().defaultSignDateAsString();
return signHeader(now);
}
protected String signHeader(final String now) {
String authzHeader = signCache.createAuthorizationHeader(signer.get(), "bench", testKeyFingerprint, keyPair, now);
return authzHeader;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment