Last active
March 20, 2017 17:36
-
-
Save cburroughs/85b452c132014fe0e2c725901f2f994f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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