diff --git a/.gitignore b/.gitignore index 9a195a6..400a076 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,6 @@ NtyCo/ .vscode/ -img/ *.db *.copy proactor copy.c -ntyco copy.c -reactor copy.c diff --git a/README.md b/README.md index 2f8f4eb..fbf62ec 100644 --- a/README.md +++ b/README.md @@ -10,19 +10,41 @@ save() -> 全保存数据集。 1. 会出现大量的内存碎片,实现一个内存池,管理内存数据。 2. 对比有内存池和没有内存池的性能差异,以及开源内存池 jemalloc 的性能差别。 `sudo apt-get install -y libjemalloc-dev` -3. 性能测: - 1. qps 有没有内存池。 - ![alt text](img/无内存池.png) - 2.虚拟内存的占用情况 htop。插入百万条数据集(KV*100w,set 200w del 100w delete 200w set 100w)。 - ![alt text](img/未运行状态.png) - ![alt text](img/200w插入100w删除.png) - ![alt text](img/100w插入200w删除.png) - ![alt text](img/无内存池200w条插入100w删除.png) - 无内存池: --> time_used=1488789 ms, ops=6000000, qps=4030 - ![alt text](img/mpalloc200w插入100w删除.png) - 自实现内存池: --> time_used=1439444 ms, ops=6000000, qps=4168 +测试: + +虚拟内存的占用情况 htop。插入百万条数据集(KV*100w,set 200w del 100w delete 200w set 100w)。 + +##### 单条插入 +未运行状态 VIRT 24.8M +![alt text](img/未运行状态.png) + + + +无内存池: --> time_used=1488789 ms, ops=6000000, qps=4030\ +VIRT 105M +![alt text](img/无内存池200w插入100w删除.png) + +自实现内存池: --> time_used=1439444 ms, ops=6000000, qps=4168\ +VIRT 135M +![alt text](img/mpalloc200w插入100w删除.png) +![alt text](img/jemalloc2000w插入100w删除.png) +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. A_0: mp_alloc (3+8B) -> 16B, malloc (3B) -> 8B diff --git a/img/jemalloc2000w插入100w删除.png b/img/jemalloc2000w插入100w删除.png new file mode 100644 index 0000000..177c7fa Binary files /dev/null and b/img/jemalloc2000w插入100w删除.png differ diff --git a/img/mpalloc200w插入100w删除.png b/img/mpalloc200w插入100w删除.png new file mode 100644 index 0000000..6d665d7 Binary files /dev/null and b/img/mpalloc200w插入100w删除.png differ diff --git a/img/批处理jemalloc.png b/img/批处理jemalloc.png new file mode 100644 index 0000000..d26f54c Binary files /dev/null and b/img/批处理jemalloc.png differ diff --git a/img/批处理无内存池.png b/img/批处理无内存池.png new file mode 100644 index 0000000..ebec3b3 Binary files /dev/null and b/img/批处理无内存池.png differ diff --git a/img/批处理自实现内存池.png b/img/批处理自实现内存池.png new file mode 100644 index 0000000..fe61f3d Binary files /dev/null and b/img/批处理自实现内存池.png differ diff --git a/img/无内存池200w插入100w删除.png b/img/无内存池200w插入100w删除.png new file mode 100644 index 0000000..3a049ca Binary files /dev/null and b/img/无内存池200w插入100w删除.png differ diff --git a/img/未运行状态.png b/img/未运行状态.png new file mode 100644 index 0000000..e0738d7 Binary files /dev/null and b/img/未运行状态.png differ diff --git a/mem_pool/mem_pool.c b/mem_pool/mem_pool.c index 947eb08..bf2bb92 100644 --- a/mem_pool/mem_pool.c +++ b/mem_pool/mem_pool.c @@ -26,7 +26,6 @@ void kvs_free(void *ptr) { #if MEMORY_SELECT_MALLOC == MEMORY_USE_MYMALLOC mp_pool_t global_mempool = {0}; -#endif // >= x 的第一个2^n 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; node->next = p->buckets[cid].free_list; p->buckets[cid].free_list = node; -} \ No newline at end of file +} +#endif diff --git a/mem_pool/mem_pool.h b/mem_pool/mem_pool.h index 584aebb..300a89c 100644 --- a/mem_pool/mem_pool.h +++ b/mem_pool/mem_pool.h @@ -5,7 +5,7 @@ #define MEMORY_USE_MYMALLOC 1 #define MEMORY_USE_JEMALLOC 2 -#define MEMORY_SELECT_MALLOC MEMORY_USE_MYMALLOC +#define MEMORY_SELECT_MALLOC MEMORY_USE_JEMALLOC #include #include diff --git a/test/test_client.h b/test/test_client.h index 6aafe16..3ec095a 100644 --- a/test/test_client.h +++ b/test/test_client.h @@ -16,7 +16,7 @@ #define CMD_SIZE (1024) #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 PRESP print_response diff --git a/test/testcase.c b/test/testcase.c index 0545f0a..bb0246f 100644 --- a/test/testcase.c +++ b/test/testcase.c @@ -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}; // 组 batch(最多 64 条) - for(int i = 0;i < 48; ++ i){ + for(int i = 0;i < 100; ++ i){ if(value == NULL){ int klen = sprintf(bkey, "%s%d", key, i); 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){ 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); }else if(mode == -1){ save(connfd); + }else if(mode == 4){ + batch_qps(connfd); } return 0; } \ No newline at end of file