RYW benchmark in C
This commit is contained in:
parent
1b1a0d27e2
commit
03fe86aa1f
|
@ -0,0 +1,295 @@
|
||||||
|
/*
|
||||||
|
* ryw_benchmark.c
|
||||||
|
*
|
||||||
|
* This source file is part of the FoundationDB open source project
|
||||||
|
*
|
||||||
|
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||||
|
*
|
||||||
|
* Licensed 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
#define FDB_API_VERSION 500
|
||||||
|
#include <foundationdb/fdb_c.h>
|
||||||
|
#include <foundationdb/fdb_c_options.g.h>
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
pthread_t netThread;
|
||||||
|
|
||||||
|
void preload(FDBTransaction *tr, int numKeys) {
|
||||||
|
fdb_transaction_clear_range(tr, (uint8_t*)"", 0, (uint8_t*)"\xff", 1);
|
||||||
|
|
||||||
|
uint32_t i;
|
||||||
|
for(i = 0; i < numKeys; ++i) {
|
||||||
|
uint32_t k = htonl(i);
|
||||||
|
fdb_transaction_set(tr, (uint8_t*)&k, 4, (uint8_t*)&k, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void* runNetwork() {
|
||||||
|
checkError(fdb_run_network(), "run network");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
FDBDatabase* openDatabase() {
|
||||||
|
checkError(fdb_setup_network(), "setup network");
|
||||||
|
pthread_create(&netThread, NULL, &runNetwork, NULL);
|
||||||
|
|
||||||
|
FDBFuture *f = fdb_create_cluster(NULL);
|
||||||
|
checkError(fdb_future_block_until_ready(f), "block for cluster");
|
||||||
|
|
||||||
|
FDBCluster *cluster;
|
||||||
|
checkError(fdb_future_get_cluster(f, &cluster), "get cluster");
|
||||||
|
|
||||||
|
fdb_future_destroy(f);
|
||||||
|
|
||||||
|
f = fdb_cluster_create_database(cluster, (uint8_t*)"DB", 2);
|
||||||
|
checkError(fdb_future_block_until_ready(f), "block for database");
|
||||||
|
|
||||||
|
FDBDatabase *db;
|
||||||
|
checkError(fdb_future_get_database(f, &db), "get database");
|
||||||
|
|
||||||
|
fdb_future_destroy(f);
|
||||||
|
fdb_cluster_destroy(cluster);
|
||||||
|
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
int numKeys = 10000;
|
||||||
|
int keySize = 16;
|
||||||
|
uint8_t** keys;
|
||||||
|
|
||||||
|
void populateKeys() {
|
||||||
|
keys = (uint8_t**)malloc(sizeof(uint8_t*)*(numKeys+1));
|
||||||
|
|
||||||
|
uint32_t i;
|
||||||
|
for(i = 0; i <= numKeys; ++i) {
|
||||||
|
keys[i] = malloc(keySize);
|
||||||
|
sprintf((char*)keys[i], "%0*d", keySize, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void insertData(FDBTransaction *tr) {
|
||||||
|
fdb_transaction_clear_range(tr, (uint8_t*)"", 0, (uint8_t*)"\xff", 1);
|
||||||
|
|
||||||
|
uint8_t *v = (uint8_t*)"foo";
|
||||||
|
uint32_t i;
|
||||||
|
for(i = 0; i <= numKeys; ++i) {
|
||||||
|
fdb_transaction_set(tr, keys[i], keySize, v, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int runTest(int (*testFxn)(FDBTransaction*), FDBTransaction *tr, struct ResultSet *rs, const char *kpiName) {
|
||||||
|
int numRuns = 1;
|
||||||
|
int *results = malloc(sizeof(int)*numRuns);
|
||||||
|
int i = 0;
|
||||||
|
for(; i < numRuns; ++i) {
|
||||||
|
results[i] = testFxn(tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = median(results, numRuns);
|
||||||
|
free(results);
|
||||||
|
|
||||||
|
addKpi(rs, kpiName, result, "keys/s");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getSingle(FDBTransaction *tr) {
|
||||||
|
int present;
|
||||||
|
uint8_t const *value;
|
||||||
|
int length;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
double start = getTime();
|
||||||
|
for(i = 0; i < numKeys; ++i) {
|
||||||
|
FDBFuture *f = fdb_transaction_get(tr, keys[5001], keySize, 0);
|
||||||
|
checkError(fdb_future_block_until_ready(f), "block for get");
|
||||||
|
checkError(fdb_future_get_value(f, &present, &value, &length), "get result");
|
||||||
|
fdb_future_destroy(f);
|
||||||
|
}
|
||||||
|
double end = getTime();
|
||||||
|
|
||||||
|
return numKeys / (end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getManySequential(FDBTransaction *tr) {
|
||||||
|
int present;
|
||||||
|
uint8_t const *value;
|
||||||
|
int length;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
double start = getTime();
|
||||||
|
for(i = 0; i < numKeys; ++i) {
|
||||||
|
FDBFuture *f = fdb_transaction_get(tr, keys[i], keySize, 0);
|
||||||
|
checkError(fdb_future_block_until_ready(f), "block for get");
|
||||||
|
checkError(fdb_future_get_value(f, &present, &value, &length), "get result");
|
||||||
|
fdb_future_destroy(f);
|
||||||
|
}
|
||||||
|
double end = getTime();
|
||||||
|
|
||||||
|
return numKeys / (end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getRangeBasic(FDBTransaction *tr) {
|
||||||
|
int count;
|
||||||
|
const FDBKeyValue *kvs;
|
||||||
|
int more;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
double start = getTime();
|
||||||
|
for(i = 0; i < 100; ++i) {
|
||||||
|
FDBFuture *f = fdb_transaction_get_range(tr, FDB_KEYSEL_LAST_LESS_OR_EQUAL(keys[0], keySize), FDB_KEYSEL_LAST_LESS_OR_EQUAL(keys[numKeys], keySize), numKeys, 0, 0, 1, 0, 0);
|
||||||
|
|
||||||
|
checkError(fdb_future_block_until_ready(f), "block for get range");
|
||||||
|
checkError(fdb_future_get_keyvalue_array(f, &kvs, &count, &more), "get range results");
|
||||||
|
|
||||||
|
if(count != numKeys) {
|
||||||
|
fprintf(stderr, "Bad count %d (expected %d)\n", count, numKeys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
double end = getTime();
|
||||||
|
|
||||||
|
return 100 * numKeys / (end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
int singleClearGetRange(FDBTransaction *tr) {
|
||||||
|
int count;
|
||||||
|
const FDBKeyValue *kvs;
|
||||||
|
int more;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < numKeys; i+=2) {
|
||||||
|
fdb_transaction_clear(tr, keys[i], keySize);
|
||||||
|
}
|
||||||
|
|
||||||
|
double start = getTime();
|
||||||
|
for(i = 0; i < 100; ++i) {
|
||||||
|
FDBFuture *f = fdb_transaction_get_range(tr, FDB_KEYSEL_LAST_LESS_OR_EQUAL(keys[0], keySize), FDB_KEYSEL_LAST_LESS_OR_EQUAL(keys[numKeys], keySize), numKeys, 0, 0, 1, 0, 0);
|
||||||
|
|
||||||
|
checkError(fdb_future_block_until_ready(f), "block for get range");
|
||||||
|
checkError(fdb_future_get_keyvalue_array(f, &kvs, &count, &more), "get range results");
|
||||||
|
|
||||||
|
fdb_future_destroy(f);
|
||||||
|
|
||||||
|
if(count != numKeys/2) {
|
||||||
|
fprintf(stderr, "Bad count %d (expected %d)\n", count, numKeys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
double end = getTime();
|
||||||
|
|
||||||
|
insertData(tr);
|
||||||
|
return 100 * numKeys / 2 / (end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
int clearRangeGetRange(FDBTransaction *tr) {
|
||||||
|
int count;
|
||||||
|
const FDBKeyValue *kvs;
|
||||||
|
int more;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < numKeys; i+=4) {
|
||||||
|
fdb_transaction_clear_range(tr, keys[i], keySize, keys[i+1], keySize);
|
||||||
|
}
|
||||||
|
|
||||||
|
double start = getTime();
|
||||||
|
for(i = 0; i < 100; ++i) {
|
||||||
|
FDBFuture *f = fdb_transaction_get_range(tr, FDB_KEYSEL_LAST_LESS_OR_EQUAL(keys[0], keySize), FDB_KEYSEL_LAST_LESS_OR_EQUAL(keys[numKeys], keySize), numKeys, 0, 0, 1, 0, 0);
|
||||||
|
|
||||||
|
checkError(fdb_future_block_until_ready(f), "block for get range");
|
||||||
|
checkError(fdb_future_get_keyvalue_array(f, &kvs, &count, &more), "get range results");
|
||||||
|
|
||||||
|
fdb_future_destroy(f);
|
||||||
|
|
||||||
|
if(count != numKeys*3/4) {
|
||||||
|
fprintf(stderr, "Bad count %d (expected %d)\n", count, numKeys*3/4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
double end = getTime();
|
||||||
|
|
||||||
|
insertData(tr);
|
||||||
|
return 100 * numKeys * 3 / 4 / (end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
int interleavedSetsGets(FDBTransaction *tr) {
|
||||||
|
int present;
|
||||||
|
uint8_t const *value;
|
||||||
|
int length;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
uint8_t *k = (uint8_t*)"foo";
|
||||||
|
uint8_t v[10];
|
||||||
|
int num = 1;
|
||||||
|
|
||||||
|
double start = getTime();
|
||||||
|
sprintf((char*)v, "%d", num);
|
||||||
|
fdb_transaction_set(tr, k, 3, v, strlen((char*)v));
|
||||||
|
|
||||||
|
for(i = 0; i < 10000; ++i) {
|
||||||
|
FDBFuture *f = fdb_transaction_get(tr, k, 3, 0);
|
||||||
|
checkError(fdb_future_block_until_ready(f), "block for get");
|
||||||
|
checkError(fdb_future_get_value(f, &present, &value, &length), "get result");
|
||||||
|
fdb_future_destroy(f);
|
||||||
|
|
||||||
|
sprintf((char*)v, "%d", ++num);
|
||||||
|
fdb_transaction_set(tr, k, 3, v, strlen((char*)v));
|
||||||
|
}
|
||||||
|
double end = getTime();
|
||||||
|
|
||||||
|
return 10000 / (end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ResultSet* runTests() {
|
||||||
|
struct ResultSet *rs = newResultSet();
|
||||||
|
FDBDatabase *db = openDatabase();
|
||||||
|
|
||||||
|
FDBTransaction *tr;
|
||||||
|
checkError(fdb_database_create_transaction(db, &tr), "create transaction");
|
||||||
|
|
||||||
|
FDBFuture *f = fdb_transaction_get_read_version(tr);
|
||||||
|
checkError(fdb_future_block_until_ready(f), "block for read version");
|
||||||
|
|
||||||
|
int64_t version;
|
||||||
|
checkError(fdb_future_get_version(f, &version), "get version");
|
||||||
|
fdb_future_destroy(f);
|
||||||
|
|
||||||
|
insertData(tr);
|
||||||
|
|
||||||
|
runTest(&getSingle, tr, rs, "C: get single cached value throughput");
|
||||||
|
runTest(&getManySequential, tr, rs, "C: get sequential cached values throughput");
|
||||||
|
runTest(&getRangeBasic, tr, rs, "C: get range cached values throughput");
|
||||||
|
runTest(&singleClearGetRange, tr, rs, "C: get range cached values with clears throughput");
|
||||||
|
runTest(&clearRangeGetRange, tr, rs, "C: get range cached values with clear ranges throughput");
|
||||||
|
runTest(&interleavedSetsGets, tr, rs, "C: interleaved sets and gets on a single key throughput");
|
||||||
|
|
||||||
|
fdb_database_destroy(db);
|
||||||
|
fdb_stop_network();
|
||||||
|
|
||||||
|
return rs;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
checkError(fdb_select_api_version(500), "select API version");
|
||||||
|
printf("Running RYW Benchmark test at client version: %s\n", fdb_get_client_version());
|
||||||
|
|
||||||
|
populateKeys();
|
||||||
|
struct ResultSet *rs = runTests();
|
||||||
|
writeResultSet(rs);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
/*
|
||||||
|
* test.h
|
||||||
|
*
|
||||||
|
* This source file is part of the FoundationDB open source project
|
||||||
|
*
|
||||||
|
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||||||
|
*
|
||||||
|
* Licensed 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
double getTime() {
|
||||||
|
static struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
return tv.tv_usec/1000000.0 + tv.tv_sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getError(int err, const char* context) {
|
||||||
|
if(err) {
|
||||||
|
fprintf(stderr, "Error in %s: %d\n", context, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkError(int err, const char* context) {
|
||||||
|
if(getError(err, context)) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int cmpfunc(const void* a, const void* b) {
|
||||||
|
return (*(int*)a - *(int*)b);
|
||||||
|
}
|
||||||
|
|
||||||
|
int median(int *values, int length) {
|
||||||
|
qsort(values, length, sizeof(int), cmpfunc);
|
||||||
|
return values[length/2];
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Kpi {
|
||||||
|
const char *name;
|
||||||
|
int value;
|
||||||
|
const char *units;
|
||||||
|
|
||||||
|
struct Kpi *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Error {
|
||||||
|
const char *message;
|
||||||
|
|
||||||
|
struct Error *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ResultSet {
|
||||||
|
struct Kpi *kpis;
|
||||||
|
struct Error *errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ResultSet* newResultSet() {
|
||||||
|
struct ResultSet *rs = malloc(sizeof(struct ResultSet));
|
||||||
|
|
||||||
|
rs->kpis = NULL;
|
||||||
|
rs->errors = NULL;
|
||||||
|
|
||||||
|
return rs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addKpi(struct ResultSet *rs, const char *name, int value, const char *units) {
|
||||||
|
struct Kpi *k = malloc(sizeof(struct Kpi));
|
||||||
|
k->name = name;
|
||||||
|
k->value = value;
|
||||||
|
k->units = units;
|
||||||
|
k->next = rs->kpis;
|
||||||
|
rs->kpis = k;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addError(struct ResultSet *rs, const char *message) {
|
||||||
|
struct Error *e = malloc(sizeof(struct Error));
|
||||||
|
e->message = message;
|
||||||
|
e->next = rs->errors;
|
||||||
|
rs->errors = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeResultSet(struct ResultSet *rs) {
|
||||||
|
srand(time(NULL)); // TODO: move this?
|
||||||
|
uint64_t id = ((uint64_t)rand() << 32) + rand();
|
||||||
|
char name[100];
|
||||||
|
sprintf(name, "fdb-c_result-%llu.json", id);
|
||||||
|
FILE *fp = fopen(name, "w");
|
||||||
|
if(!fp) {
|
||||||
|
fprintf(stderr, "Could not open results file %s\n", name);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fp, "{\n");
|
||||||
|
fprintf(fp, "\t\"kpis\": {\n");
|
||||||
|
|
||||||
|
struct Kpi *k = rs->kpis;
|
||||||
|
while(k != NULL) {
|
||||||
|
fprintf(fp, "\t\t\"%s\": { \"units\": \"%s\", \"value\": %d }", k->name, k->units, k->value);
|
||||||
|
if(k->next != NULL) {
|
||||||
|
fprintf(fp, ",");
|
||||||
|
}
|
||||||
|
fprintf(fp, "\n");
|
||||||
|
k = k->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fp, "\t},\n");
|
||||||
|
fprintf(fp, "\t\"errors\": [\n");
|
||||||
|
|
||||||
|
struct Error *e = rs->errors;
|
||||||
|
while(e != NULL) {
|
||||||
|
fprintf(fp, "\t\t%s", e->message);
|
||||||
|
if(e->next != NULL) {
|
||||||
|
fprintf(fp, ",");
|
||||||
|
}
|
||||||
|
fprintf(fp, "\n");
|
||||||
|
e = e->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fp, "\t]\n");
|
||||||
|
fprintf(fp, "}\n");
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue