Skip to content

Commit 7f42bc9

Browse files
committed
feat(bench): transform/detransform benchmarks
1 parent 494e6f4 commit 7f42bc9

File tree

3 files changed

+335
-0
lines changed

3 files changed

+335
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright 2023 Aiven Oy
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.aiven.kafka.tieredstorage.benchs;
18+
19+
import javax.crypto.Cipher;
20+
import javax.crypto.NoSuchPaddingException;
21+
import javax.crypto.spec.GCMParameterSpec;
22+
import javax.crypto.spec.SecretKeySpec;
23+
24+
import java.security.InvalidAlgorithmParameterException;
25+
import java.security.InvalidKeyException;
26+
import java.security.NoSuchAlgorithmException;
27+
import java.security.SecureRandom;
28+
import java.util.Random;
29+
30+
public class AesKeyAware {
31+
protected static int ivSize;
32+
protected static SecretKeySpec secretKey;
33+
protected static byte[] aad;
34+
35+
public static void initCrypto() {
36+
// These are tests, we don't need a secure source of randomness.
37+
final Random random = new Random();
38+
39+
final byte[] dataKey = new byte[32];
40+
random.nextBytes(dataKey);
41+
secretKey = new SecretKeySpec(dataKey, "AES");
42+
43+
aad = new byte[32];
44+
random.nextBytes(aad);
45+
46+
ivSize = encryptionCipherSupplier().getIV().length;
47+
}
48+
49+
protected static Cipher encryptionCipherSupplier() {
50+
try {
51+
final Cipher encryptCipher = getCipher();
52+
encryptCipher.init(Cipher.ENCRYPT_MODE, secretKey, SecureRandom.getInstanceStrong());
53+
encryptCipher.updateAAD(aad);
54+
return encryptCipher;
55+
} catch (final NoSuchAlgorithmException | InvalidKeyException e) {
56+
throw new RuntimeException(e);
57+
}
58+
}
59+
60+
protected static Cipher decryptionCipherSupplier(final byte[] encryptedChunk) {
61+
try {
62+
final Cipher encryptCipher = getCipher();
63+
encryptCipher.init(Cipher.DECRYPT_MODE, secretKey,
64+
new GCMParameterSpec(128, encryptedChunk, 0, ivSize),
65+
SecureRandom.getInstanceStrong());
66+
encryptCipher.updateAAD(aad);
67+
return encryptCipher;
68+
} catch (final NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException e) {
69+
throw new RuntimeException(e);
70+
}
71+
}
72+
73+
protected static Cipher getCipher() {
74+
try {
75+
return Cipher.getInstance("AES/GCM/NoPadding");
76+
} catch (final NoSuchAlgorithmException | NoSuchPaddingException e) {
77+
throw new RuntimeException(e);
78+
}
79+
}
80+
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Copyright 2023 Aiven Oy
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.aiven.kafka.tieredstorage.benchs.transform;
18+
19+
import java.io.ByteArrayInputStream;
20+
import java.io.IOException;
21+
import java.io.SequenceInputStream;
22+
import java.nio.file.Files;
23+
import java.nio.file.Path;
24+
import java.security.SecureRandom;
25+
import java.util.concurrent.TimeUnit;
26+
27+
import io.aiven.kafka.tieredstorage.benchs.AesKeyAware;
28+
import io.aiven.kafka.tieredstorage.manifest.index.ChunkIndex;
29+
import io.aiven.kafka.tieredstorage.transform.BaseDetransformChunkEnumeration;
30+
import io.aiven.kafka.tieredstorage.transform.BaseTransformChunkEnumeration;
31+
import io.aiven.kafka.tieredstorage.transform.CompressionChunkEnumeration;
32+
import io.aiven.kafka.tieredstorage.transform.DecompressionChunkEnumeration;
33+
import io.aiven.kafka.tieredstorage.transform.DecryptionChunkEnumeration;
34+
import io.aiven.kafka.tieredstorage.transform.DetransformChunkEnumeration;
35+
import io.aiven.kafka.tieredstorage.transform.DetransformFinisher;
36+
import io.aiven.kafka.tieredstorage.transform.EncryptionChunkEnumeration;
37+
import io.aiven.kafka.tieredstorage.transform.TransformChunkEnumeration;
38+
import io.aiven.kafka.tieredstorage.transform.TransformFinisher;
39+
40+
import org.openjdk.jmh.annotations.Benchmark;
41+
import org.openjdk.jmh.annotations.BenchmarkMode;
42+
import org.openjdk.jmh.annotations.Fork;
43+
import org.openjdk.jmh.annotations.Level;
44+
import org.openjdk.jmh.annotations.Measurement;
45+
import org.openjdk.jmh.annotations.Mode;
46+
import org.openjdk.jmh.annotations.OutputTimeUnit;
47+
import org.openjdk.jmh.annotations.Param;
48+
import org.openjdk.jmh.annotations.Scope;
49+
import org.openjdk.jmh.annotations.Setup;
50+
import org.openjdk.jmh.annotations.State;
51+
import org.openjdk.jmh.annotations.TearDown;
52+
import org.openjdk.jmh.annotations.Warmup;
53+
import org.openjdk.jmh.profile.AsyncProfiler;
54+
import org.openjdk.jmh.runner.Runner;
55+
import org.openjdk.jmh.runner.options.Options;
56+
import org.openjdk.jmh.runner.options.OptionsBuilder;
57+
58+
@State(Scope.Benchmark)
59+
@Fork(value = 1)
60+
@Warmup(iterations = 4)
61+
@Measurement(iterations = 16)
62+
@BenchmarkMode({Mode.Throughput, Mode.SampleTime})
63+
@OutputTimeUnit(TimeUnit.MILLISECONDS)
64+
public class DetransformBench extends AesKeyAware {
65+
static Path segmentPath;
66+
@Param({"10485760", "104857600", "1073741824"})
67+
public int contentLength; // 10MiB, 100MiB, 1GiB
68+
@Param({"102400", "1048576", "5242880"})
69+
public int chunkSize; // 100KiB, 1MiB, 5MiB
70+
@Param({"false", "true"})
71+
public boolean compression;
72+
@Param({"false", "true"})
73+
public boolean encryption;
74+
75+
byte[] uploadedData;
76+
ChunkIndex chunkIndex;
77+
78+
@Setup(Level.Trial)
79+
public void setup() throws IOException {
80+
segmentPath = Files.createTempFile("segment", ".log");
81+
// to fill with random bytes.
82+
final SecureRandom secureRandom = new SecureRandom();
83+
try (final var out = Files.newOutputStream(segmentPath)) {
84+
final byte[] bytes = new byte[contentLength];
85+
secureRandom.nextBytes(bytes);
86+
out.write(bytes);
87+
}
88+
if (encryption) {
89+
initCrypto();
90+
}
91+
92+
// Transform.
93+
TransformChunkEnumeration transformEnum = new BaseTransformChunkEnumeration(
94+
Files.newInputStream(segmentPath),
95+
chunkSize
96+
);
97+
if (compression) {
98+
transformEnum = new CompressionChunkEnumeration(transformEnum);
99+
}
100+
if (encryption) {
101+
transformEnum = new EncryptionChunkEnumeration(transformEnum, AesKeyAware::encryptionCipherSupplier);
102+
}
103+
final var transformFinisher = TransformFinisher.newBuilder(transformEnum, contentLength).build();
104+
try (final var sis = new SequenceInputStream(transformFinisher)) {
105+
uploadedData = sis.readAllBytes();
106+
chunkIndex = transformFinisher.chunkIndex();
107+
}
108+
}
109+
110+
@TearDown
111+
public void teardown() throws IOException {
112+
Files.deleteIfExists(segmentPath);
113+
}
114+
115+
@Benchmark
116+
public int test() throws IOException {
117+
// Detransform.
118+
DetransformChunkEnumeration detransformEnum = new BaseDetransformChunkEnumeration(
119+
new ByteArrayInputStream(uploadedData), chunkIndex.chunks());
120+
if (encryption) {
121+
detransformEnum = new DecryptionChunkEnumeration(
122+
detransformEnum, ivSize, AesKeyAware::decryptionCipherSupplier);
123+
}
124+
if (compression) {
125+
detransformEnum = new DecompressionChunkEnumeration(detransformEnum);
126+
}
127+
final var detransformFinisher = new DetransformFinisher(detransformEnum);
128+
try (final var sis = new SequenceInputStream(detransformFinisher)) {
129+
final var bytes = sis.readAllBytes();
130+
return bytes.length;
131+
}
132+
}
133+
134+
public static void main(final String[] args) throws Exception {
135+
final Options opts = new OptionsBuilder()
136+
.include(DetransformBench.class.getSimpleName())
137+
.addProfiler(AsyncProfiler.class, "output=flamegraph")
138+
.build();
139+
new Runner(opts).run();
140+
}
141+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* Copyright 2023 Aiven Oy
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.aiven.kafka.tieredstorage.benchs.transform;
18+
19+
import java.io.IOException;
20+
import java.io.SequenceInputStream;
21+
import java.nio.file.Files;
22+
import java.nio.file.Path;
23+
import java.security.SecureRandom;
24+
import java.util.concurrent.TimeUnit;
25+
26+
import io.aiven.kafka.tieredstorage.benchs.AesKeyAware;
27+
import io.aiven.kafka.tieredstorage.transform.BaseTransformChunkEnumeration;
28+
import io.aiven.kafka.tieredstorage.transform.CompressionChunkEnumeration;
29+
import io.aiven.kafka.tieredstorage.transform.EncryptionChunkEnumeration;
30+
import io.aiven.kafka.tieredstorage.transform.TransformChunkEnumeration;
31+
import io.aiven.kafka.tieredstorage.transform.TransformFinisher;
32+
33+
import org.openjdk.jmh.annotations.Benchmark;
34+
import org.openjdk.jmh.annotations.BenchmarkMode;
35+
import org.openjdk.jmh.annotations.Fork;
36+
import org.openjdk.jmh.annotations.Level;
37+
import org.openjdk.jmh.annotations.Measurement;
38+
import org.openjdk.jmh.annotations.Mode;
39+
import org.openjdk.jmh.annotations.OutputTimeUnit;
40+
import org.openjdk.jmh.annotations.Param;
41+
import org.openjdk.jmh.annotations.Scope;
42+
import org.openjdk.jmh.annotations.Setup;
43+
import org.openjdk.jmh.annotations.State;
44+
import org.openjdk.jmh.annotations.TearDown;
45+
import org.openjdk.jmh.annotations.Warmup;
46+
import org.openjdk.jmh.profile.AsyncProfiler;
47+
import org.openjdk.jmh.runner.Runner;
48+
import org.openjdk.jmh.runner.options.Options;
49+
import org.openjdk.jmh.runner.options.OptionsBuilder;
50+
51+
@State(Scope.Benchmark)
52+
@Fork(value = 1)
53+
@Warmup(iterations = 4)
54+
@Measurement(iterations = 16)
55+
@BenchmarkMode({Mode.Throughput, Mode.SampleTime})
56+
@OutputTimeUnit(TimeUnit.MILLISECONDS)
57+
public class TransformBench extends AesKeyAware {
58+
static Path segmentPath;
59+
@Param({"10485760", "104857600", "1073741824"})
60+
public int contentLength; // 10MiB, 100MiB, 1GiB
61+
@Param({"102400", "1048576", "5242880"})
62+
public int chunkSize; // 100KiB, 1MiB, 5MiB
63+
@Param({"false", "true"})
64+
public boolean compression;
65+
@Param({"false", "true"})
66+
public boolean encryption;
67+
68+
@Setup(Level.Trial)
69+
public void setup() throws IOException {
70+
segmentPath = Files.createTempFile("segment", ".log");
71+
// to fill with random bytes.
72+
final SecureRandom secureRandom = new SecureRandom();
73+
try (final var out = Files.newOutputStream(segmentPath)) {
74+
final byte[] bytes = new byte[contentLength];
75+
secureRandom.nextBytes(bytes);
76+
out.write(bytes);
77+
}
78+
if (encryption) {
79+
initCrypto();
80+
}
81+
}
82+
83+
@TearDown
84+
public void teardown() throws IOException {
85+
Files.deleteIfExists(segmentPath);
86+
}
87+
88+
@Benchmark
89+
public byte[] test() throws IOException {
90+
// Transform.
91+
TransformChunkEnumeration transformEnum = new BaseTransformChunkEnumeration(
92+
Files.newInputStream(segmentPath),
93+
chunkSize
94+
);
95+
if (compression) {
96+
transformEnum = new CompressionChunkEnumeration(transformEnum);
97+
}
98+
if (encryption) {
99+
transformEnum = new EncryptionChunkEnumeration(transformEnum, AesKeyAware::encryptionCipherSupplier);
100+
}
101+
final var transformFinisher = TransformFinisher.newBuilder(transformEnum, contentLength).build();
102+
try (final var sis = new SequenceInputStream(transformFinisher)) {
103+
return sis.readAllBytes();
104+
}
105+
}
106+
107+
public static void main(final String[] args) throws Exception {
108+
final Options opts = new OptionsBuilder()
109+
.include(TransformBench.class.getSimpleName())
110+
.addProfiler(AsyncProfiler.class, "output=flamegraph")
111+
.build();
112+
new Runner(opts).run();
113+
}
114+
}

0 commit comments

Comments
 (0)