Skip to content

Commit 3010158

Browse files
committed
Add a benchmark for Redis
1 parent 0a430d8 commit 3010158

8 files changed

Lines changed: 259 additions & 0 deletions

File tree

‎WORKSPACE‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ maven_install(
132132
"io.netty:netty-buffer:4.1.79.Final",
133133
"io.netty:netty5-common:5.0.0.Alpha4",
134134
"io.netty:netty5-buffer:5.0.0.Alpha4",
135+
"io.lettuce:lettuce-core:6.2.0.RELEASE",
135136
],
136137
fetch_sources = True,
137138
repositories = maven_repositories,

‎src/com/komanov/redis/BUILD‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package(default_visibility = ["//src/com/komanov/redis:__subpackages__"])
2+
3+
scala_library(
4+
name = "redis",
5+
srcs = glob([
6+
"*.scala",
7+
"*.java",
8+
]),
9+
deps = [
10+
"@offheap_maven//:io_lettuce_lettuce_core",
11+
],
12+
)

‎src/com/komanov/redis/README.md‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
## Redis Benchmark for Set (SISMEMBER command)
2+
3+
Prepare data:
4+
```
5+
bazel run //src/com/komanov/redis/bin -- set-1m ~/uuid1m.txt
6+
bazel run //src/com/komanov/redis/bin -- set-10m ~/uuid10m.txt
7+
bazel run //src/com/komanov/redis/bin -- set-100k ~/uuid100k.txt
8+
```
9+
10+
Run Redis in docker:
11+
```
12+
docker run --name my-redis -p 6379:6379 redis:7.0-alpine
13+
```
14+
15+
Run cli:
16+
```
17+
docker exec -it my-redis redis-cli
18+
```
19+
20+
Run benchmark:
21+
```
22+
time bazel run //src/com/komanov/redis/perf set-1m ~/uuid1m.txt
23+
time bazel run //src/com/komanov/redis/perf set-10m ~/uuid10m.txt
24+
time bazel run //src/com/komanov/redis/perf set-100k ~/uuid100k.txt
25+
```
26+
27+
Results: https://docs.google.com/spreadsheets/d/1D5fhP-rxuxamOl58cGk7yLiLiViZnxWcE71klp6JC3I
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.komanov.redis
2+
3+
import io.lettuce.core.codec.{RedisCodec, StringCodec}
4+
5+
import java.nio.ByteBuffer
6+
import java.util.UUID
7+
8+
class StringUuidCodec extends RedisCodec[String, UUID] {
9+
override def decodeKey(bytes: ByteBuffer): String =
10+
StringCodec.ASCII.decodeKey(bytes)
11+
12+
override def decodeValue(bytes: ByteBuffer): UUID =
13+
new UUID(bytes.getLong, bytes.getLong)
14+
15+
override def encodeKey(key: String): ByteBuffer =
16+
StringCodec.ASCII.encodeKey(key)
17+
18+
override def encodeValue(value: UUID): ByteBuffer = {
19+
val bb = ByteBuffer.allocate(16)
20+
bb.putLong(value.getMostSignificantBits)
21+
bb.putLong(value.getLeastSignificantBits)
22+
bb.rewind()
23+
bb
24+
}
25+
}
26+
27+
object StringUuidCodec extends StringUuidCodec

‎src/com/komanov/redis/bin/BUILD‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
scala_binary(
2+
name = "bin",
3+
srcs = ["DataFiller.scala"],
4+
main_class = "com.komanov.redis.bin.DataFiller",
5+
deps = [
6+
"//src/com/komanov/redis",
7+
"@offheap_maven//:io_lettuce_lettuce_core",
8+
],
9+
)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.komanov.redis.bin
2+
3+
import com.komanov.redis.StringUuidCodec
4+
import io.lettuce.core.RedisClient
5+
import io.lettuce.core.api.StatefulRedisConnection
6+
7+
import java.nio.file.{Files, Paths}
8+
import java.util.UUID
9+
import scala.collection.mutable
10+
11+
// Usage: SET_NAME INPUT_FILE
12+
object DataFiller extends App {
13+
14+
val Array(setName, input) = args
15+
16+
val inputPath = Paths.get(input)
17+
require(Files.exists(inputPath), s"INPUT_FILE doesn't exist: $input")
18+
19+
val client = RedisClient.create("redis://localhost:6379")
20+
val connection: StatefulRedisConnection[String, UUID] = client.connect(StringUuidCodec);
21+
val sync = connection.sync();
22+
23+
val chunk = mutable.ListBuffer[UUID]()
24+
25+
def dumpChunk(): Unit = {
26+
val num = sync.sadd(setName, chunk.toSeq: _*)
27+
require(num == chunk.size, s"Failed to SADD: $num != ${chunk.size}")
28+
chunk.clear()
29+
}
30+
31+
Files.lines(inputPath).forEach { line =>
32+
chunk += UUID.fromString(line)
33+
if (chunk.size == 1000) {
34+
dumpChunk()
35+
}
36+
}
37+
if (chunk.nonEmpty) {
38+
dumpChunk()
39+
}
40+
41+
client.shutdown()
42+
43+
}

‎src/com/komanov/redis/perf/BUILD‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
java_binary(
2+
name = "perf",
3+
srcs = ["PerfTester.java"],
4+
main_class = "com.komanov.redis.perf.PerfTester",
5+
deps = [
6+
"//src/com/komanov/redis",
7+
"@offheap_maven//:io_lettuce_lettuce_core",
8+
],
9+
)
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package com.komanov.redis.perf;
2+
3+
import com.komanov.redis.StringUuidCodec$;
4+
import io.lettuce.core.RedisClient;
5+
import io.lettuce.core.api.StatefulRedisConnection;
6+
import io.lettuce.core.api.async.RedisAsyncCommands;
7+
8+
import java.nio.file.Files;
9+
import java.nio.file.Paths;
10+
import java.util.ArrayList;
11+
import java.util.Arrays;
12+
import java.util.List;
13+
import java.util.UUID;
14+
import java.util.concurrent.CountDownLatch;
15+
import java.util.concurrent.LinkedBlockingDeque;
16+
import java.util.stream.Collectors;
17+
18+
public class PerfTester {
19+
private static class InFlight {
20+
public final int index;
21+
public long startTime;
22+
23+
public InFlight(int index) {
24+
this.index = index;
25+
}
26+
}
27+
28+
public static void main(String[] args) throws Throwable {
29+
assert args.length == 2;
30+
31+
String setName = args[0];
32+
List<UUID> uuids = Files.readAllLines(Paths.get(args[1]))
33+
.stream()
34+
.map(UUID::fromString)
35+
.limit(1_000_000)
36+
.collect(Collectors.toList());
37+
38+
RedisClient client = RedisClient.create("redis://localhost:6379");
39+
StatefulRedisConnection<String, UUID> connection = client.connect(StringUuidCodec$.MODULE$);
40+
RedisAsyncCommands<String, UUID> async = connection.async();
41+
42+
System.out.println("About to run warmup");
43+
int warmupCount = 10_000;
44+
CountDownLatch latch = new CountDownLatch(warmupCount);
45+
uuids.stream().limit(warmupCount).forEach(v -> {
46+
async.sismember(setName, v).handle((r, e) -> {
47+
latch.countDown();
48+
return r;
49+
});
50+
});
51+
System.out.println("Awaiting for warmup to complete");
52+
latch.await();
53+
System.out.println("Warmup completed!");
54+
55+
ArrayList<ArrayList<String>> results = new ArrayList<>();
56+
for (int parallelism = 0; parallelism <= 90; parallelism += 10) {
57+
results.add(runBenchmark(async, uuids, setName, parallelism == 0 ? 1 : parallelism));
58+
}
59+
for (int parallelism = 100; parallelism <= 1000; parallelism += 100) {
60+
results.add(runBenchmark(async, uuids, setName, parallelism));
61+
}
62+
results.add(runBenchmark(async, uuids, setName, 2000));
63+
64+
for (int num = 0; num < results.get(0).size(); ++num) {
65+
for (int i = 0; i < results.size(); ++i) {
66+
ArrayList<String> single = results.get(i);
67+
System.out.print(single.get(num) + "\t");
68+
}
69+
System.out.println();
70+
}
71+
72+
connection.close();
73+
client.shutdown();
74+
}
75+
76+
private static ArrayList<String> runBenchmark(RedisAsyncCommands<String, UUID> async, List<UUID> uuids, String setName, int parallelism)
77+
throws Throwable {
78+
long[] durations = new long[uuids.size()];
79+
LinkedBlockingDeque<InFlight> queue = new LinkedBlockingDeque<>(parallelism);
80+
long overallStartTime = System.nanoTime();
81+
for (int i = 0; i < uuids.size(); i++) {
82+
final InFlight item = new InFlight(i);
83+
queue.put(item);
84+
item.startTime = System.nanoTime();
85+
async
86+
.sismember(setName, uuids.get(i))
87+
.handle((isMember, e) -> {
88+
if (e == null) {
89+
if (!isMember) {
90+
System.out.println("NOT MEMBER!");
91+
System.exit(1);
92+
}
93+
durations[item.index] = System.nanoTime() - item.startTime;
94+
if (!queue.remove(item)) {
95+
System.out.println("NOT IN QUEUE!");
96+
System.exit(2);
97+
}
98+
} else {
99+
e.printStackTrace();
100+
System.exit(1);
101+
}
102+
return Boolean.TRUE;
103+
})
104+
;
105+
}
106+
107+
while (!queue.isEmpty()) {
108+
Thread.sleep(0);
109+
}
110+
111+
long overallDuration = System.nanoTime() - overallStartTime;
112+
113+
ArrayList<String> results = new ArrayList<>();
114+
results.add("" + uuids.size());
115+
results.add("" + parallelism);
116+
results.add("" + overallDuration);
117+
results.add("" + overallDuration / uuids.size());
118+
119+
Arrays.sort(durations);
120+
Arrays.stream(durations).limit(5).forEach(d -> {
121+
results.add("" + d);
122+
});
123+
Arrays.stream(durations).skip(durations.length / 2).limit(5).forEach(d -> {
124+
results.add("" + d);
125+
});
126+
Arrays.stream(durations).skip(durations.length - 5).forEach(d -> {
127+
results.add("" + d);
128+
});
129+
return results;
130+
}
131+
}

0 commit comments

Comments
 (0)