zvfs: 性能测试

This commit is contained in:
2026-02-24 16:01:29 +00:00
parent 6f8f2148c3
commit 8d1d9506cd
7 changed files with 779 additions and 48 deletions

View File

@@ -1,12 +1,113 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <stdint.h>
int main(int argc, char **argv)
static double time_diff_sec(struct timespec a, struct timespec b)
{
const char *path = "/zvfs/func_test.dat";
return (b.tv_sec - a.tv_sec) +
(b.tv_nsec - a.tv_nsec) / 1000000000.0;
}
static int test_single_file_perf(const char *path)
{
// size_t io_size = 128 * 1024;
size_t io_size = 4096;
size_t max_size = 2ULL * 1024 * 1024 * 1024; /* 最大 2GB循环覆写 */
size_t max_count = max_size / io_size;
int test_sec = 10;
int direct = 0;
printf("\n=== test_single_file_perf ===\n");
printf("Path : %s\n", path);
printf("IO size : %zu KB\n", io_size / 1024);
printf("Max file: %zu MB\n", max_size / 1024 / 1024);
printf("Duration: %d sec\n", test_sec);
unlink(path);
char *buf = aligned_alloc(4096, io_size);
if (!buf) { perror("aligned_alloc"); return 1; }
memset(buf, 'A', io_size);
struct timespec t1, t2, now;
/* ================= WRITE ================= */
int fd = open(path, O_CREAT | O_RDWR | direct, 0644);
if (fd < 0) { perror("open write"); free(buf); return 1; }
clock_gettime(CLOCK_MONOTONIC, &t1);
size_t wcount = 0;
size_t wpos = 0; /* 当前写位置(以块为单位) */
do {
/* 超过最大文件大小seek 回头循环覆写 */
if (wpos >= max_count) {
lseek(fd, 0, SEEK_SET);
wpos = 0;
}
if (write(fd, buf, io_size) != (ssize_t)io_size) {
perror("write");
close(fd);
free(buf);
return 2;
}
wcount++;
wpos++;
clock_gettime(CLOCK_MONOTONIC, &now);
} while (time_diff_sec(t1, now) < test_sec);
clock_gettime(CLOCK_MONOTONIC, &t2);
close(fd);
double wsec = time_diff_sec(t1, t2);
double wmb = (double)(wcount * io_size) / (1024.0 * 1024.0);
printf("\nWRITE:\n");
printf(" total : %.1f MB\n", wmb);
printf(" time : %.3f sec\n", wsec);
printf(" IOPS : %.0f ops/sec\n", wcount / wsec);
printf(" BW : %.2f MB/s\n", wmb / wsec);
/* ================= READ ================= */
fd = open(path, O_RDONLY | direct);
if (fd < 0) { perror("open read"); free(buf); return 3; }
clock_gettime(CLOCK_MONOTONIC, &t1);
size_t rcount = 0;
do {
ssize_t r = read(fd, buf, io_size);
if (r <= 0) {
lseek(fd, 0, SEEK_SET);
continue;
}
rcount++;
clock_gettime(CLOCK_MONOTONIC, &now);
} while (time_diff_sec(t1, now) < test_sec);
clock_gettime(CLOCK_MONOTONIC, &t2);
close(fd);
double rsec = time_diff_sec(t1, t2);
double rmb = (double)(rcount * io_size) / (1024.0 * 1024.0);
printf("\nREAD:\n");
printf(" total : %.1f MB\n", rmb);
printf(" time : %.3f sec\n", rsec);
printf(" IOPS : %.0f ops/sec\n", rcount / rsec);
printf(" BW : %.2f MB/s\n", rmb / rsec);
unlink(path);
free(buf);
return 0;
}
/* ------------------------------------------------------------------ */
/* Test 1: 原有基础测试 */
/* ------------------------------------------------------------------ */
static int test_basic(const char *path)
{
printf("\n=== test_basic ===\n");
printf("open: %s\n", path);
int fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644);
@@ -24,7 +125,6 @@ int main(int argc, char **argv)
close(fd);
fd = open(path, O_RDONLY);
if (fd < 0) { perror("open R"); return 3; }
@@ -45,4 +145,266 @@ int main(int argc, char **argv)
if (unlink(path) != 0) { perror("unlink"); return 5; }
printf("unlink: ok\n");
return 0;
}
/* ------------------------------------------------------------------ */
/* Test 2: lseek */
/* ------------------------------------------------------------------ */
static int test_lseek(const char *path)
{
printf("\n=== test_lseek ===\n");
int fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644);
if (fd < 0) { perror("open"); return 1; }
/* 写入 26 个字母 */
const char *alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (write(fd, alpha, 26) != 26) { perror("write"); return 2; }
printf("write 26 bytes: %s\n", alpha);
/* SEEK_SET: 跳到第 0 字节,读 5 个 */
off_t pos = lseek(fd, 0, SEEK_SET);
printf("lseek SEEK_SET 0 -> %ld\n", (long)pos);
char buf[32] = {0};
ssize_t r = read(fd, buf, 5);
printf("read 5 bytes: %.*s (expect: ABCDE)\n", (int)r, buf);
/* SEEK_CUR: 从当前位置5再向前跳 3 个,应读到第 8 字节 'I' */
pos = lseek(fd, 3, SEEK_CUR);
printf("lseek SEEK_CUR +3 -> %ld\n", (long)pos);
memset(buf, 0, sizeof(buf));
r = read(fd, buf, 5);
printf("read 5 bytes: %.*s (expect: IJKLM)\n", (int)r, buf);
/* SEEK_END: 从文件尾往回 5 字节,读到最后 5 个字母 */
pos = lseek(fd, -5, SEEK_END);
printf("lseek SEEK_END -5 -> %ld\n", (long)pos);
memset(buf, 0, sizeof(buf));
r = read(fd, buf, 10); /* 最多只剩 5 字节 */
printf("read %zd bytes: %.*s (expect: VWXYZ)\n", r, (int)r, buf);
/* SEEK_SET 超过文件末尾:写一个字节,制造"空洞" */
pos = lseek(fd, 30, SEEK_SET);
printf("lseek SEEK_SET 30 -> %ld\n", (long)pos);
if (write(fd, "!", 1) != 1) { perror("write hole"); return 3; }
/* 重新读回空洞区域,[26,29] 应为 0x00 */
lseek(fd, 26, SEEK_SET);
memset(buf, 0xAA, sizeof(buf));
r = read(fd, buf, 5);
printf("read hole+1: %zd bytes, hole[0]=%02X hole[1]=%02X hole[2]=%02X "
"hole[3]=%02X last='%c' (expect: 00 00 00 00 '!')\n",
r, (unsigned char)buf[0], (unsigned char)buf[1],
(unsigned char)buf[2], (unsigned char)buf[3], buf[4]);
close(fd);
unlink(path);
return 0;
}
/* ------------------------------------------------------------------ */
/* Test 3: 同一文件被两个 fd 同时打开(一读一写) */
/* ------------------------------------------------------------------ */
static int test_dual_open_same_file(const char *path)
{
printf("\n=== test_dual_open_same_file ===\n");
/* 先创建并写入初始内容 */
int fd_init = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644);
if (fd_init < 0) { perror("open init"); return 1; }
const char *init = "0123456789";
if (write(fd_init, init, 10) != 10) { perror("write init"); return 2; }
close(fd_init);
/* 用写句柄和读句柄分别打开同一文件 */
int fd_w = open(path, O_WRONLY);
if (fd_w < 0) { perror("open W"); return 3; }
int fd_r = open(path, O_RDONLY);
if (fd_r < 0) { perror("open R"); return 4; }
printf("fd_w=%d fd_r=%d\n", fd_w, fd_r);
/* 通过写句柄覆写前 5 字节 */
if (write(fd_w, "HELLO", 5) != 5) { perror("write"); return 5; }
printf("write via fd_w: HELLO (overwrite first 5 bytes)\n");
/* 通过读句柄从头读回全部内容 */
char buf[32] = {0};
lseek(fd_r, 0, SEEK_SET);
ssize_t r = read(fd_r, buf, sizeof(buf));
printf("read via fd_r: %zd bytes: %.*s (expect: HELLO56789)\n",
r, (int)r, buf);
/* 继续用写句柄追加 */
lseek(fd_w, 0, SEEK_END);
if (write(fd_w, "!!!", 3) != 3) { perror("write append"); return 6; }
printf("write append via fd_w: !!!\n");
/* 读句柄读追加部分 */
lseek(fd_r, 10, SEEK_SET);
memset(buf, 0, sizeof(buf));
r = read(fd_r, buf, sizeof(buf));
printf("read appended via fd_r: %zd bytes: %.*s (expect: !!!)\n",
r, (int)r, buf);
close(fd_w);
close(fd_r);
unlink(path);
return 0;
}
/* ------------------------------------------------------------------ */
/* Test 4: 两个不同文件同时打开,分别读写 */
/* ------------------------------------------------------------------ */
static int test_two_files(const char *path_a, const char *path_b)
{
printf("\n=== test_two_files ===\n");
int fd_a = open(path_a, O_CREAT | O_RDWR | O_TRUNC, 0644);
if (fd_a < 0) { perror("open A"); return 1; }
int fd_b = open(path_b, O_CREAT | O_RDWR | O_TRUNC, 0644);
if (fd_b < 0) { perror("open B"); return 2; }
printf("fd_a=%d fd_b=%d\n", fd_a, fd_b);
/* 分别写入不同内容 */
const char *data_a = "File-A: Hello World!";
const char *data_b = "File-B: Goodbye World!";
if (write(fd_a, data_a, strlen(data_a)) < 0) { perror("write A"); return 3; }
if (write(fd_b, data_b, strlen(data_b)) < 0) { perror("write B"); return 4; }
printf("write A: %s\n", data_a);
printf("write B: %s\n", data_b);
/* 各自 seek 回头读取,验证内容互不干扰 */
lseek(fd_a, 0, SEEK_SET);
lseek(fd_b, 0, SEEK_SET);
char buf_a[64] = {0};
char buf_b[64] = {0};
ssize_t r_a = read(fd_a, buf_a, sizeof(buf_a));
ssize_t r_b = read(fd_b, buf_b, sizeof(buf_b));
printf("read A: %zd bytes: %.*s\n", r_a, (int)r_a, buf_a);
printf("read B: %zd bytes: %.*s\n", r_b, (int)r_b, buf_b);
int ok = 1;
if (strncmp(buf_a, data_a, strlen(data_a)) != 0) {
printf("FAIL: A content mismatch!\n"); ok = 0;
}
if (strncmp(buf_b, data_b, strlen(data_b)) != 0) {
printf("FAIL: B content mismatch!\n"); ok = 0;
}
if (ok) printf("PASS: both files read back correctly\n");
/* 交叉写:向 A 追加,向 B 中段覆写,再各自读回验证 */
lseek(fd_a, 0, SEEK_END);
write(fd_a, "[A-TAIL]", 8);
lseek(fd_b, 8, SEEK_SET); /* "File-B: " 之后 */
write(fd_b, "Hi! ", 7); /* 覆写 "Goodbye" */
lseek(fd_a, 0, SEEK_SET);
lseek(fd_b, 0, SEEK_SET);
memset(buf_a, 0, sizeof(buf_a));
memset(buf_b, 0, sizeof(buf_b));
r_a = read(fd_a, buf_a, sizeof(buf_a));
r_b = read(fd_b, buf_b, sizeof(buf_b));
printf("after cross-write:\n");
printf(" A: %.*s\n", (int)r_a, buf_a);
printf(" B: %.*s\n", (int)r_b, buf_b);
close(fd_a);
close(fd_b);
unlink(path_a);
unlink(path_b);
return 0;
}
static int test_write_file(const char *path)
{
printf("\n=== test_write_file ===\n");
int fd = open(path, O_CREAT | O_RDWR, 0644);
if (fd < 0) { perror("open"); return 1; }
printf("open: %s fd=%d\n", path, fd);
const char *msg = "Hello, zvfs!";
ssize_t w = write(fd, msg, strlen(msg));
if (w < 0) { perror("write"); close(fd); return 2; }
printf("write: %zd bytes: %s\n", w, msg);
close(fd);
printf("close: ok\n");
return 0;
}
static int test_read_delete_file(const char *path)
{
printf("\n=== test_read_delete_file ===\n");
int fd = open(path, O_RDONLY);
if (fd < 0) { perror("open"); return 1; }
printf("open: %s fd=%d\n", path, fd);
char buf[256] = {0};
ssize_t r = read(fd, buf, sizeof(buf));
if (r < 0) { perror("read"); close(fd); return 2; }
printf("read: %zd bytes: %.*s\n", r, (int)r, buf);
close(fd);
printf("close: ok\n");
if (unlink(path) != 0) { perror("unlink"); return 3; }
printf("unlink: ok\n");
return 0;
}
/* ------------------------------------------------------------------ */
/* main */
/* ------------------------------------------------------------------ */
int main(int argc, char **argv)
{
int rc = 0;
char path[256];
char path_a[256];
char path_b[256];
if(argc >= 2){
sprintf(path, "%s/file.dat", argv[1]);
sprintf(path_a, "%s/file_a.dat", argv[1]);
sprintf(path_b, "%s/file_a.dat", argv[1]);
}else {
sprintf(path, "/tmp/test.dat");
}
if(argc == 3){
int choose = atoi(argv[2]);
if(choose == 0){
rc = test_write_file(path);
}else if(choose == 1){
rc = test_read_delete_file(path);
}
return rc;
}
// printf("argv[0]: %s\n", argv[0]);
// printf("argv[1]: %s\n", argv[1]);
// printf("path_a: %s\n", path_a);
// printf("path_b: %s\n", path_b);
// rc |= test_basic(path);
// rc |= test_lseek(path);
// rc |= test_dual_open_same_file(path);
// rc |= test_two_files(path_a, path_b);
rc |= test_single_file_perf(path);
printf("\n=== all tests %s ===\n", rc == 0 ? "PASSED" : "FAILED");
return rc;
}