测试用例编写
3
.gitignore
vendored
@@ -1,9 +1,6 @@
|
|||||||
NtyCo/
|
NtyCo/
|
||||||
.vscode/
|
.vscode/
|
||||||
img/
|
|
||||||
*.db
|
*.db
|
||||||
*.copy
|
*.copy
|
||||||
|
|
||||||
proactor copy.c
|
proactor copy.c
|
||||||
ntyco copy.c
|
|
||||||
reactor copy.c
|
|
||||||
|
|||||||
44
README.md
@@ -10,19 +10,41 @@ save() -> 全保存数据集。
|
|||||||
1. 会出现大量的内存碎片,实现一个内存池,管理内存数据。
|
1. 会出现大量的内存碎片,实现一个内存池,管理内存数据。
|
||||||
2. 对比有内存池和没有内存池的性能差异,以及开源内存池 jemalloc 的性能差别。
|
2. 对比有内存池和没有内存池的性能差异,以及开源内存池 jemalloc 的性能差别。
|
||||||
`sudo apt-get install -y libjemalloc-dev`
|
`sudo apt-get install -y libjemalloc-dev`
|
||||||
3. 性能测:
|
|
||||||
1. qps 有没有内存池。
|
|
||||||

|
|
||||||
|
|
||||||
2.虚拟内存的占用情况 htop。插入百万条数据集(KV*100w,set 200w del 100w delete 200w set 100w)。
|
测试:
|
||||||

|
|
||||||

|
虚拟内存的占用情况 htop。插入百万条数据集(KV*100w,set 200w del 100w delete 200w set 100w)。
|
||||||

|
|
||||||

|
##### 单条插入
|
||||||
无内存池: --> time_used=1488789 ms, ops=6000000, qps=4030
|
未运行状态 VIRT 24.8M
|
||||||

|

|
||||||
自实现内存池: --> time_used=1439444 ms, ops=6000000, qps=4168
|
|
||||||
|
|
||||||
|
|
||||||
|
无内存池: --> time_used=1488789 ms, ops=6000000, qps=4030\
|
||||||
|
VIRT 105M
|
||||||
|

|
||||||
|
|
||||||
|
自实现内存池: --> time_used=1439444 ms, ops=6000000, qps=4168\
|
||||||
|
VIRT 135M
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
jemalloc: time_used=1502797 ms, ops=6000000, qps=3992\
|
||||||
|
VIRT 99M
|
||||||
|
|
||||||
|
##### 批处理
|
||||||
|
一次执行100条。
|
||||||
|
|
||||||
|
无内存池:--> time_used=142913 ms, ops=6000000, qps=41983\
|
||||||
|
VIRT 99M
|
||||||
|
|
||||||
|
自实现内存池: --> time_used=122754 ms, ops=6000000, qps=48878\
|
||||||
|
VIRT 135M
|
||||||
|
|
||||||
|
jemalloc:--> time_used=122206 ms, ops=6000000, qps=49097\
|
||||||
|
VIRT 99M
|
||||||
|
|
||||||
性能分析:
|
性能分析:
|
||||||
1. key :
|
1. key :
|
||||||
1. A_0: mp_alloc (3+8B) -> 16B, malloc (3B) -> 8B
|
1. A_0: mp_alloc (3+8B) -> 16B, malloc (3B) -> 8B
|
||||||
|
|||||||
BIN
img/jemalloc2000w插入100w删除.png
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
img/mpalloc200w插入100w删除.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
img/批处理jemalloc.png
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
img/批处理无内存池.png
Normal file
|
After Width: | Height: | Size: 95 KiB |
BIN
img/批处理自实现内存池.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
img/无内存池200w插入100w删除.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
img/未运行状态.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
@@ -26,7 +26,6 @@ void kvs_free(void *ptr) {
|
|||||||
|
|
||||||
#if MEMORY_SELECT_MALLOC == MEMORY_USE_MYMALLOC
|
#if MEMORY_SELECT_MALLOC == MEMORY_USE_MYMALLOC
|
||||||
mp_pool_t global_mempool = {0};
|
mp_pool_t global_mempool = {0};
|
||||||
#endif
|
|
||||||
|
|
||||||
// >= x 的第一个2^n
|
// >= x 的第一个2^n
|
||||||
uint32_t mp_ceil_pow2_u32(uint32_t x) {
|
uint32_t mp_ceil_pow2_u32(uint32_t x) {
|
||||||
@@ -161,4 +160,5 @@ void mp_free(void* ptr){
|
|||||||
mp_free_node_t* node = (mp_free_node_t*)ptr;
|
mp_free_node_t* node = (mp_free_node_t*)ptr;
|
||||||
node->next = p->buckets[cid].free_list;
|
node->next = p->buckets[cid].free_list;
|
||||||
p->buckets[cid].free_list = node;
|
p->buckets[cid].free_list = node;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
#define MEMORY_USE_MYMALLOC 1
|
#define MEMORY_USE_MYMALLOC 1
|
||||||
#define MEMORY_USE_JEMALLOC 2
|
#define MEMORY_USE_JEMALLOC 2
|
||||||
|
|
||||||
#define MEMORY_SELECT_MALLOC MEMORY_USE_MYMALLOC
|
#define MEMORY_SELECT_MALLOC MEMORY_USE_JEMALLOC
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
#define CMD_SIZE (1024)
|
#define CMD_SIZE (1024)
|
||||||
#define BATCH_SIZE (65536)
|
#define BATCH_SIZE (65536)
|
||||||
#define KVS_BATCH_MAX 64
|
#define KVS_BATCH_MAX 128
|
||||||
#define TIME_SUB_MS(tv1, tv2) ((tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) / 1000)
|
#define TIME_SUB_MS(tv1, tv2) ((tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) / 1000)
|
||||||
|
|
||||||
// #define PRESP print_response
|
// #define PRESP print_response
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ void do_batch_test(int fd, int op, const char *key, const char *value){
|
|||||||
char bkey[15]={0}, bval[15]={0};
|
char bkey[15]={0}, bval[15]={0};
|
||||||
|
|
||||||
// 组 batch(最多 64 条)
|
// 组 batch(最多 64 条)
|
||||||
for(int i = 0;i < 48; ++ i){
|
for(int i = 0;i < 100; ++ i){
|
||||||
if(value == NULL){
|
if(value == NULL){
|
||||||
int klen = sprintf(bkey, "%s%d", key, i);
|
int klen = sprintf(bkey, "%s%d", key, i);
|
||||||
kvs_batch_add(&batch, op, bkey, klen, NULL, 0);
|
kvs_batch_add(&batch, op, bkey, klen, NULL, 0);
|
||||||
@@ -190,6 +190,64 @@ void do_batch_test(int fd, int op, const char *key, const char *value){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void batch_qps(int connfd) {
|
||||||
|
const int N = 1000000;
|
||||||
|
const int B = 100; // do_batch_test() 里写死 50
|
||||||
|
const char *valA = "va";
|
||||||
|
const char *valB = "vb";
|
||||||
|
const char *valC = "vc";
|
||||||
|
|
||||||
|
struct timeval tv_begin, tv_end;
|
||||||
|
gettimeofday(&tv_begin, NULL);
|
||||||
|
|
||||||
|
// ---------------- Phase 1: ADD 两条 DEL 一条 (100w) ----------------
|
||||||
|
// 每轮:ADD/SET A_i, ADD/SET B_i, DEL A_i
|
||||||
|
// 用 batch:每批处理 idx..idx+49
|
||||||
|
for (int base = 0; base < N; base += B) {
|
||||||
|
// prefix 必须短,避免 do_batch_test 里 bkey[15] 溢出
|
||||||
|
// do_batch_test 会生成: prefix + i (0..49)
|
||||||
|
// 我们把 base 编进 prefix,保证全局 key 唯一
|
||||||
|
char preA[16], preB[16];
|
||||||
|
|
||||||
|
// 例:A123450_0..A123450_49(base=123450)
|
||||||
|
// 注意:这里 prefix 长度要尽量 <= 10 左右
|
||||||
|
snprintf(preA, sizeof(preA), "A%d_", base/100);
|
||||||
|
snprintf(preB, sizeof(preB), "B%d_", base/100);
|
||||||
|
|
||||||
|
do_batch_test(connfd, KVS_CMD_RSET, preA, valA); // 50次 RSET A
|
||||||
|
do_batch_test(connfd, KVS_CMD_RSET, preB, valB); // 50次 RSET B
|
||||||
|
do_batch_test(connfd, KVS_CMD_RDEL, preA, NULL); // 50次 RDEL A
|
||||||
|
|
||||||
|
if (base % 10000 == 0) printf("P1 base:%d\n", base);
|
||||||
|
}
|
||||||
|
printf("phase 1 end\n");
|
||||||
|
|
||||||
|
// ---------------- Phase 2: ADD 一条 DEL 两条 (100w) ----------------
|
||||||
|
// 每轮:ADD/SET C_i, DEL B_i, DEL C_i
|
||||||
|
for (int base = 0; base < N; base += B) {
|
||||||
|
char preB[16], preC[16];
|
||||||
|
snprintf(preB, sizeof(preB), "B%d_", base/100);
|
||||||
|
snprintf(preC, sizeof(preC), "C%d_", base/100);
|
||||||
|
|
||||||
|
do_batch_test(connfd, KVS_CMD_RSET, preC, valC); // 50次 RSET C
|
||||||
|
do_batch_test(connfd, KVS_CMD_RDEL, preB, NULL); // 50次 RDEL B
|
||||||
|
do_batch_test(connfd, KVS_CMD_RDEL, preC, NULL); // 50次 RDEL C
|
||||||
|
|
||||||
|
if (base % 10000 == 0) printf("P2 base:%d\n", base);
|
||||||
|
}
|
||||||
|
printf("phase 2 end\n");
|
||||||
|
|
||||||
|
gettimeofday(&tv_end, NULL);
|
||||||
|
int time_used = TIME_SUB_MS(tv_end, tv_begin);
|
||||||
|
|
||||||
|
// 真实总 ops 还是 6*N (每轮 6 个操作)
|
||||||
|
long long ops = (long long)N * 6;
|
||||||
|
long long qps = (time_used > 0) ? (ops * 1000 / time_used) : 0;
|
||||||
|
|
||||||
|
printf("BATCH(do_batch_test) ADD2-DEL1 then ADD1-DEL2 (N=%d) --> time_used=%d ms, ops=%lld, qps=%lld\n",
|
||||||
|
N, time_used, ops, qps);
|
||||||
|
}
|
||||||
|
|
||||||
void save(int connfd){
|
void save(int connfd){
|
||||||
testcase(connfd, KVS_CMD_SAVE, NULL, 0, NULL, 0, KVS_STATUS_OK, NULL, 0, "SAVE");
|
testcase(connfd, KVS_CMD_SAVE, NULL, 0, NULL, 0, KVS_STATUS_OK, NULL, 0, "SAVE");
|
||||||
}
|
}
|
||||||
@@ -303,6 +361,8 @@ int main(int argc, char *argv[]) {
|
|||||||
do_batch_test(connfd, KVS_CMD_HEXIST, "hash_set", NULL);
|
do_batch_test(connfd, KVS_CMD_HEXIST, "hash_set", NULL);
|
||||||
}else if(mode == -1){
|
}else if(mode == -1){
|
||||||
save(connfd);
|
save(connfd);
|
||||||
|
}else if(mode == 4){
|
||||||
|
batch_qps(connfd);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||