Add benchmarks for SSLEngine implementations

Motivation:

As we provide our own SSLEngine implementation we should have benchmarks to compare it against JDK impl.

Modifications:

Add benchmarks for wrap / unwrap and handshake performance.

Result:

Benchmarks FTW.
This commit is contained in:
Norman Maurer 2017-02-02 20:01:42 +01:00
parent 7aff6b0330
commit d73477c7bd
9 changed files with 574 additions and 0 deletions

View File

@ -106,6 +106,12 @@
<artifactId>Agrona</artifactId>
<version>0.5.1</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>${tcnative.artifactId}</artifactId>
<classifier>${tcnative.classifier}</classifier>
<optional>false</optional>
</dependency>
</dependencies>
<build>

View File

@ -0,0 +1,279 @@
/*
* Copyright 2017 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.microbench.handler.ssl;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.microbench.util.AbstractMicrobenchmark;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.internal.PlatformDependent;
import org.openjdk.jmh.annotations.Param;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLException;
import java.io.File;
import java.nio.ByteBuffer;
public class AbstractSslEngineBenchmark extends AbstractMicrobenchmark {
private static final String PROTOCOL_TLS_V1_2 = "TLSv1.2";
public enum SslEngineProvider {
JDK {
@Override
SslProvider sslProvider() {
return SslProvider.JDK;
}
},
OPENSSL {
@Override
SslProvider sslProvider() {
return SslProvider.OPENSSL;
}
},
OPENSSL_REFCNT {
@Override
SslProvider sslProvider() {
return SslProvider.OPENSSL_REFCNT;
}
};
private final SslContext clientContext = newClientContext();
private final SslContext serverContext = newServerContext();
private SslContext newClientContext() {
try {
return SslContextBuilder.forClient()
.sslProvider(sslProvider())
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();
} catch (SSLException e) {
throw new IllegalStateException(e);
}
}
private SslContext newServerContext() {
try {
File keyFile = new File(getClass().getResource("test_unencrypted.pem").getFile());
File crtFile = new File(getClass().getResource("test.crt").getFile());
return SslContextBuilder.forServer(crtFile, keyFile)
.sslProvider(sslProvider())
.build();
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
SSLEngine newClientEngine(String cipher) {
return configureEngine(clientContext.newEngine(PooledByteBufAllocator.DEFAULT), cipher);
}
SSLEngine newServerEngine(String cipher) {
return configureEngine(serverContext.newEngine(PooledByteBufAllocator.DEFAULT), cipher);
}
abstract SslProvider sslProvider();
static SSLEngine configureEngine(SSLEngine engine, String cipher) {
engine.setEnabledProtocols(new String[]{ PROTOCOL_TLS_V1_2 });
engine.setEnabledCipherSuites(new String[]{ cipher });
return engine;
}
}
public enum BufferType {
HEAP {
@Override
ByteBuffer newBuffer(int size) {
return ByteBuffer.allocate(size);
}
},
DIRECT {
@Override
ByteBuffer newBuffer(int size) {
return ByteBuffer.allocateDirect(size);
}
@Override
void freeBuffer(ByteBuffer buffer) {
PlatformDependent.freeDirectBuffer(buffer);
}
};
abstract ByteBuffer newBuffer(int size);
void freeBuffer(ByteBuffer buffer) { }
}
@Param
public SslEngineProvider sslProvider;
@Param
public BufferType bufferType;
// Includes cipher required by HTTP/2
@Param({ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" })
public String cipher;
protected SSLEngine clientEngine;
protected SSLEngine serverEngine;
private ByteBuffer cTOs;
private ByteBuffer sTOc;
private ByteBuffer serverAppReadBuffer;
private ByteBuffer clientAppReadBuffer;
private ByteBuffer empty;
protected final void initEngines() {
clientEngine = newClientEngine();
serverEngine = newServerEngine();
}
protected final void destroyEngines() {
ReferenceCountUtil.release(clientEngine);
ReferenceCountUtil.release(serverEngine);
}
protected final void initHandshakeBuffers() {
cTOs = allocateBuffer(clientEngine.getSession().getPacketBufferSize());
sTOc = allocateBuffer(serverEngine.getSession().getPacketBufferSize());
serverAppReadBuffer = allocateBuffer(
serverEngine.getSession().getApplicationBufferSize());
clientAppReadBuffer = allocateBuffer(
clientEngine.getSession().getApplicationBufferSize());
empty = allocateBuffer(0);
}
protected final void destroyHandshakeBuffers() {
freeBuffer(cTOs);
freeBuffer(sTOc);
freeBuffer(serverAppReadBuffer);
freeBuffer(clientAppReadBuffer);
freeBuffer(empty);
}
protected final boolean doHandshake() throws SSLException {
clientEngine.beginHandshake();
serverEngine.beginHandshake();
SSLEngineResult clientResult = null;
SSLEngineResult serverResult = null;
boolean clientHandshakeFinished = false;
boolean serverHandshakeFinished = false;
do {
int cTOsPos = cTOs.position();
int sTOcPos = sTOc.position();
if (!clientHandshakeFinished) {
clientResult = clientEngine.wrap(empty, cTOs);
runDelegatedTasks(clientResult, clientEngine);
assert empty.remaining() == clientResult.bytesConsumed();
assert cTOs.position() - cTOsPos == clientResult.bytesProduced();
clientHandshakeFinished = isHandshakeFinished(clientResult);
}
if (!serverHandshakeFinished) {
serverResult = serverEngine.wrap(empty, sTOc);
runDelegatedTasks(serverResult, serverEngine);
assert empty.remaining() == serverResult.bytesConsumed();
assert sTOc.position() - sTOcPos == serverResult.bytesProduced();
serverHandshakeFinished = isHandshakeFinished(serverResult);
}
cTOs.flip();
sTOc.flip();
cTOsPos = cTOs.position();
sTOcPos = sTOc.position();
if (!clientHandshakeFinished) {
int clientAppReadBufferPos = clientAppReadBuffer.position();
clientResult = clientEngine.unwrap(sTOc, clientAppReadBuffer);
runDelegatedTasks(clientResult, clientEngine);
assert sTOc.position() - sTOcPos == clientResult.bytesConsumed();
assert clientAppReadBuffer.position() - clientAppReadBufferPos == clientResult.bytesProduced();
clientHandshakeFinished = isHandshakeFinished(clientResult);
} else {
assert !sTOc.hasRemaining();
}
if (!serverHandshakeFinished) {
int serverAppReadBufferPos = serverAppReadBuffer.position();
serverResult = serverEngine.unwrap(cTOs, serverAppReadBuffer);
runDelegatedTasks(serverResult, serverEngine);
assert cTOs.position() - cTOsPos == serverResult.bytesConsumed();
assert serverAppReadBuffer.position() - serverAppReadBufferPos == serverResult.bytesProduced();
serverHandshakeFinished = isHandshakeFinished(serverResult);
} else {
assert !cTOs.hasRemaining();
}
sTOc.compact();
cTOs.compact();
} while (!clientHandshakeFinished || !serverHandshakeFinished);
return clientResult.getStatus() == SSLEngineResult.Status.OK &&
serverResult.getStatus() == SSLEngineResult.Status.OK;
}
protected final SSLEngine newClientEngine() {
return sslProvider.newClientEngine(cipher);
}
protected final SSLEngine newServerEngine() {
return sslProvider.newServerEngine(cipher);
}
protected static boolean checkSslEngineResult(SSLEngineResult result, ByteBuffer src, ByteBuffer dst) {
return result.getStatus() == SSLEngineResult.Status.OK && !src.hasRemaining() && dst.hasRemaining();
}
protected final ByteBuffer allocateBuffer(int size) {
return bufferType.newBuffer(size);
}
protected final void freeBuffer(ByteBuffer buffer) {
bufferType.freeBuffer(buffer);
}
private static boolean isHandshakeFinished(SSLEngineResult result) {
return result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED;
}
private static void runDelegatedTasks(SSLEngineResult result, SSLEngine engine) {
if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
for (;;) {
Runnable task = engine.getDelegatedTask();
if (task == null) {
break;
}
task.run();
}
}
}
}

View File

@ -0,0 +1,79 @@
/*
* Copyright 2017 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.microbench.handler.ssl;
import io.netty.util.internal.ThreadLocalRandom;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.TearDown;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLException;
import java.nio.ByteBuffer;
public abstract class AbstractSslEngineThroughputBenchmark extends AbstractSslEngineBenchmark {
@Param({ "64", "128", "512", "1024", "4096" })
public int messageSize;
protected ByteBuffer wrapSrcBuffer;
private ByteBuffer wrapDstBuffer;
@Setup(Level.Iteration)
public final void setup() throws Exception {
initEngines();
initHandshakeBuffers();
wrapDstBuffer = allocateBuffer(clientEngine.getSession().getPacketBufferSize());
wrapSrcBuffer = allocateBuffer(messageSize);
byte[] bytes = new byte[messageSize];
ThreadLocalRandom.current().nextBytes(bytes);
wrapSrcBuffer.put(bytes);
wrapSrcBuffer.flip();
// Complete the initial TLS handshake.
if (!doHandshake()) {
throw new IllegalStateException();
}
doSetup();
}
protected void doSetup() throws Exception { }
@TearDown(Level.Iteration)
public final void tearDown() throws Exception {
destroyEngines();
destroyHandshakeBuffers();
freeBuffer(wrapSrcBuffer);
freeBuffer(wrapDstBuffer);
doTearDown();
}
protected void doTearDown() throws Exception { }
protected final ByteBuffer doWrap() throws SSLException {
wrapSrcBuffer.position(0).limit(messageSize);
wrapDstBuffer.clear();
SSLEngineResult wrapResult = clientEngine.wrap(wrapSrcBuffer, wrapDstBuffer);
assert checkSslEngineResult(wrapResult, wrapSrcBuffer, wrapDstBuffer);
return wrapDstBuffer;
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright 2017 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.microbench.handler.ssl;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLException;
import java.nio.ByteBuffer;
@State(Scope.Benchmark)
@Threads(1)
public class SslEngineEchoBenchmark extends AbstractSslEngineThroughputBenchmark {
private ByteBuffer unwrapDstBuffer;
@Override
protected void doSetup() {
unwrapDstBuffer = allocateBuffer(serverEngine.getSession().getApplicationBufferSize());
}
@Override
protected void doTearDown() {
freeBuffer(unwrapDstBuffer);
}
@Benchmark
public ByteBuffer wrapUnwrap() throws SSLException {
ByteBuffer src = doWrap();
src.flip();
unwrapDstBuffer.clear();
SSLEngineResult unwrapResult = serverEngine.unwrap(src, unwrapDstBuffer);
assert checkSslEngineResult(unwrapResult, src, unwrapDstBuffer);
return unwrapDstBuffer;
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2017 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.microbench.handler.ssl;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Threads;
import java.util.concurrent.TimeUnit;
@State(Scope.Benchmark)
@Threads(1)
public class SslEngineHandshakeBenchmark extends AbstractSslEngineBenchmark {
@Setup(Level.Iteration)
public void setup() {
// Init an engine one time and create the buffers needed for an handshake so we can use them in the benchmark
initEngines();
initHandshakeBuffers();
destroyEngines();
}
@TearDown(Level.Iteration)
public void teardown() {
destroyHandshakeBuffers();
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean handshake() throws Exception {
initEngines();
boolean ok = doHandshake();
destroyEngines();
assert ok;
return ok;
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2017 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.microbench.handler.ssl;
import javax.net.ssl.SSLException;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import java.nio.ByteBuffer;
@State(Scope.Benchmark)
@Threads(1)
public class SslEngineWrapBenchmark extends AbstractSslEngineThroughputBenchmark {
@Benchmark
public ByteBuffer wrap() throws SSLException {
return doWrap();
}
}

View File

@ -0,0 +1,19 @@
/*
* Copyright 2017 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Benchmarks for SSL.
*/
package io.netty.microbench.handler.ssl;

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIC/jCCAeagAwIBAgIIIMONxElm0AIwDQYJKoZIhvcNAQELBQAwPjE8MDoGA1UE
AwwzZThhYzAyZmEwZDY1YTg0MjE5MDE2MDQ1ZGI4YjA1YzQ4NWI0ZWNkZi5uZXR0
eS50ZXN0MCAXDTEzMDgwMjA3NTEzNloYDzk5OTkxMjMxMjM1OTU5WjA+MTwwOgYD
VQQDDDNlOGFjMDJmYTBkNjVhODQyMTkwMTYwNDVkYjhiMDVjNDg1YjRlY2RmLm5l
dHR5LnRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDb+HBO3C0U
RBKvDUgJHbhIlBye8X/cbNH3lDq3XOOFBz7L4XZKLDIXS+FeQqSAUMo2otmU+Vkj
0KorshMjbUXfE1KkTijTMJlaga2M2xVVt21fRIkJNWbIL0dWFLWyRq7OXdygyFkI
iW9b2/LYaePBgET22kbtHSCAEj+BlSf265+1rNxyAXBGGGccCKzEbcqASBKHOgVp
6pLqlQAfuSy6g/OzGzces3zXRrGu1N3pBIzAIwCW429n52ZlYfYR0nr+REKDnRrP
IIDsWASmEHhBezTD+v0qCJRyLz2usFgWY+7agUJE2yHHI2mTu2RAFngBilJXlMCt
VwT0xGuQxkbHAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEv8N7Xm8qaY2FgrOc6P
a1GTgA+AOb3aU33TGwAR86f+nLf6BSPaohcQfOeJid7FkFuYInuXl+oqs+RqM/j8
R0E5BuGYY2wOKpL/PbFi1yf/Kyvft7KVh8e1IUUec/i1DdYTDB0lNWvXXxjfMKGL
ct3GMbEHKvLfHx42Iwz/+fva6LUrO4u2TDfv0ycHuR7UZEuC1DJ4xtFhbpq/QRAj
CyfNx3cDc7L2EtJWnCmivTFA9l8MF1ZPMDSVd4ecQ7B0xZIFQ5cSSFt7WGaJCsGM
zYkU4Fp4IykQcWxdlNX7wJZRwQ2TZJFFglpTiFZdeq6I6Ad9An1Encpz5W8UJ4tv
hmw=
-----END CERTIFICATE-----

View File

@ -0,0 +1,24 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDb+HBO3C0URBKvDUgJHbhIlBye
8X/cbNH3lDq3XOOFBz7L4XZKLDIXS+FeQqSAUMo2otmU+Vkj0KorshMjbUXfE1KkTijTMJlaga2M
2xVVt21fRIkJNWbIL0dWFLWyRq7OXdygyFkIiW9b2/LYaePBgET22kbtHSCAEj+BlSf265+1rNxy
AXBGGGccCKzEbcqASBKHOgVp6pLqlQAfuSy6g/OzGzces3zXRrGu1N3pBIzAIwCW429n52ZlYfYR
0nr+REKDnRrPIIDsWASmEHhBezTD+v0qCJRyLz2usFgWY+7agUJE2yHHI2mTu2RAFngBilJXlMCt
VwT0xGuQxkbHAgMBAAECggEBAJJdKaVfXWNptCDkLnVaYB9y5eRgfppVkhQxfiw5023Vl1QjrgjG
hYH4zHli0IBMwXA/RZWZoFVzZ3dxoshk0iQPgGKxWvrDEJcnSCo8MGL7jPvh52jILp6uzsGZQBji
bTgFPmOBS7ShdgZiQKD9PD2psrmqHZ1yTwjIm5cGfzQM8Y6tjm0xLBn676ecJNdS1TL10y9vmSUM
Ofdkmeg9Z9TEK95lP2fF/NIcxCo0LF9JcHUvTuYBDnBH0XMZi0w0ZcRReMSdAZ2lLiXgBeCO53el
2NIrtkRx+qOvLua9UfwO2h/0rs66ZeV0YuFCjv067nytyZf2zhU/QbCHRypzfrkCgYEA/facuAJs
6MQKsNvhozoBeDRMkrZPMh8Sb0w50EqzIGz3pdms6UvCiggoMbhxKOwuYWZ689fBPGwm7x0RdwDO
jyUuEbFnQFe+CpdHy6VK7vIQed1SwAcdTMDwCYbkJNglqHEB7qUYYTFLr8okGyWVdthUoh4IAubU
TR3TFbGraDUCgYEA3bwJ/UNA5pHtb/nh4/dNL7/bRMwXyPZPpC5z+gjjgUMgsSRBz8+iPNTB4iSQ
1j9zm+pnXGi35zWZcI4jvIcFusb08eS7xcZDb+7X2r2wenLNmyuTOa1812y233FicU+ah91fa9aD
yUfTjj3GFawbgNNhMyWa3aEMV+c73t6sKosCgYEA35oQZhsMlOx2lT0jrzlVLeauPMZzeCfPbVrp
1DDRAg2vBcFf8pCXmjyQVyaTy3oXY/585tDh/DclGIa5Z9O4CmSr6TwPMqGOW3jS58SC81sBkqqB
Pz2EWJ3POjQgDyiYD3RgRSPrETf78azCmXw/2sGh0pMqbpOZ/MPzpDgoOLkCgYEAsdv4g09kCs75
Dz34hRzErE2P+8JePdPdlEuyudhRbUlEOvNjWucpMvRSRSyhhUnGWUWP/V7+TRcAanmJjtsbrHOU
3Udlm0HqrCmAubQ4kC/wXsx4Pua7Yi2RDvBrT4rT4LGgreaXNWhI+Srx7kZslUx5Bkbez3I0bXpM
2vvwS/sCgYAducNt1KC4W7jzMWUivvuy5hQQmX/G0JHtu1pfv9cmA8agnc1I/r7xoirftuSG25Pm
r+eP5SKbKb8ZQlp10JeBkNnk8eAG8OkQyBaECYDBadEr1/LK2LmIEjYKzKAjYQ4cX2KMtY271jjX
WrzzXNqBdThFfMHiJE8k9xYmaLDKhQ==
-----END PRIVATE KEY-----