Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create benchmark comparison script against Redis #143

Merged
merged 4 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ test-win:
cover:
go tool cover -html=./coverage/coverage.out

benchmark:
go run redis_benchmark.go $(if $(commands),-commands="$(commands)") $(if $(use_local_server),-use_local_server)
34 changes: 10 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,31 +155,17 @@ Redis clients.

<a name="benchmarks"></a>
# Benchmarks
The following benchmark only applies to the TCP client-server mode.
To compare command performance with Redis, benchmarks can be run with:

Hardware: MacBook Pro 14in, M1 chip, 16GB RAM, 8 Cores <br/>
Command: `redis-benchmark -h localhost -p 7480 -q -t ping,set,get,incr,lpush,rpush,lpop,rpop,sadd,hset,zpopmin,lrange,mset` <br/>
Result:
```
PING_INLINE: 89285.71 requests per second, p50=0.247 msec
PING_MBULK: 85543.20 requests per second, p50=0.239 msec
SET: 65573.77 requests per second, p50=0.455 msec
GET: 79176.56 requests per second, p50=0.295 msec
INCR: 68870.52 requests per second, p50=0.439 msec
LPUSH: 27601.44 requests per second, p50=1.567 msec
RPUSH: 61842.92 requests per second, p50=0.519 msec
LPOP: 58548.01 requests per second, p50=0.567 msec
RPOP: 68681.32 requests per second, p50=0.439 msec
SADD: 67613.25 requests per second, p50=0.479 msec
HSET: 56561.09 requests per second, p50=0.599 msec
ZPOPMIN: 70972.32 requests per second, p50=0.359 msec
LPUSH (needed to benchmark LRANGE): 26434.05 requests per second, p50=1.623 msec
LRANGE_100 (first 100 elements): 26939.66 requests per second, p50=1.263 msec
LRANGE_300 (first 300 elements): 5081.82 requests per second, p50=9.095 msec
LRANGE_500 (first 500 elements): 2554.87 requests per second, p50=18.191 msec
LRANGE_600 (first 600 elements): 1903.96 requests per second, p50=24.607 msec
MSET (10 keys): 56022.41 requests per second, p50=0.463 msec
```
`make benchmark`

Prerequisites:
- `brew install redis` to run the Redis server and benchmark script
- `brew tap echovault/echovault` & `brew install echovault/echovault/echovault` to run the SugarDB Client-Server
NicoleStrel marked this conversation as resolved.
Show resolved Hide resolved

Benchmark script options:
- `make benchmark use_local_server=true` runs on your local SugarDB Client-Server
- `make benchmark commands=ping,set,get...` runs the benchmark script on the specified commands

<a name="commands"></a>
# Supported Commands
Expand Down
141 changes: 141 additions & 0 deletions redis_benchmark.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package main

import (
"flag"
"fmt"
"os"
"os/exec"
"strings"
"text/tabwriter"
"time"
)

const (
Host = "localhost"
SugarDBPort = "7480"
RedisPort = "6379"
)

type Metrics struct {
CommandName string
RequestsPerSecond string
P50Latency string
}

func getCommandArgs() (string, bool) {
defaultCommands := "ping,set,get,incr,lpush,rpush,lpop,rpop,sadd,hset,zpopmin,lrange,mset"
commands := flag.String("commands", defaultCommands, "Commands to run")
useLocal := flag.Bool("use_local_server", false, "Run benchamark using local SugarDB server")
flag.Parse()
fmt.Printf("Provided commands: %s\n", *commands)
if *useLocal {
fmt.Println("Using local running SugarDB server")
}
return *commands, *useLocal
}

func runBenchmark(port string, commands string) ([]Metrics, error) {
var results []Metrics

// Run redis-benchmark
cmd := exec.Command("redis-benchmark", "-h", Host, "-p", port, "-q", "-t", commands)
output, err := cmd.CombinedOutput()
if err != nil {
return nil, err
}

strOutput := string(output)
fmt.Println(strOutput)
lines := strings.Split(strOutput, "\n")
for _, line := range lines {
if !strings.HasPrefix(line, "WARNING") && line != "" {
// Get command name
colonIndex := strings.Index(line, ":")
commandName := line[:colonIndex]

// Get requests per second
reqSecIndex := strings.Index(line, " requests per second")
spaceIndex := strings.LastIndex(line[:reqSecIndex], " ")
requestsPerSecond := line[spaceIndex+1 : reqSecIndex]

// Get p50 latency
p50Index := strings.Index(line, "p50=")
spaceAfterP50 := strings.Index(line[p50Index:], " ")
p50Latency := line[p50Index+4 : p50Index+spaceAfterP50]

results = append(results, Metrics{
CommandName: commandName,
RequestsPerSecond: requestsPerSecond,
P50Latency: p50Latency,
})
}
}

return results, nil
}

func createDisplayTable(redisResults []Metrics, sugarDBResults []Metrics) {
if len(sugarDBResults) != len(redisResults) {
fmt.Println("Error: Number of commands in Redis and SugarDB do not match")
}

fmt.Println("Benchmark Performance Results:")
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
fmt.Fprint(w, "Command\tRedis (req/sec)\tRedis p50 Latency (msec)\tSugarDB (req/sec)\tSugarDB p50 Latency (msec)\t\n")
for i := 0; i < len(redisResults); i++ {
command := redisResults[i].CommandName
redisReqSec := redisResults[i].RequestsPerSecond
redisLatency := redisResults[i].P50Latency
sugarDBReqSec := sugarDBResults[i].RequestsPerSecond
sugarDBLatency := sugarDBResults[i].P50Latency

fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t\n", command, redisReqSec, redisLatency, sugarDBReqSec, sugarDBLatency)
}
w.Flush()
}

func main() {

commands, useLocal := getCommandArgs()

// Start a local Redis server, wait a few seconds for it to start
exec.Command("redis-server", "--port", RedisPort).Start()
time.Sleep(2 * time.Second)

// Run benchmark on local Redis server
fmt.Println("-------Running Redis Benchmarks------")
redisResults, err := runBenchmark(RedisPort, commands)
if err != nil {
fmt.Println("Error running benchmark on Redis server:", err)
return
}

if !useLocal {
// Run the packaged SugarDB server, wait a few seconds for it to start
exec.Command("echovault", "--bind-addr=localhost", "--data-dir=persistence").Start()
time.Sleep(5 * time.Second)
}

// Run benchmark on SugarDB server
fmt.Println("-------Running SugarDB Benchmarks------")
sugarDBResults, err := runBenchmark(SugarDBPort, commands)
if err != nil {
fmt.Println("Error running benchmark on SugarDB server:", err)
fmt.Println("Check that the SugarDB server is running")
return
}

// Display results in a table format
createDisplayTable(redisResults, sugarDBResults)

// Kill the local Redis server
exec.Command("pkill", "-f", "redis-server").Run()

if !useLocal {
// Kill the packaged SugarDB server
exec.Command("pkill", "-f", "echovault").Run()
if err := os.RemoveAll("persistence"); err != nil { // Remove persistence directory
fmt.Println("Error removing persistence directory:", err)
}
}
}