1+ // ==============================================
2+ //
3+ // 本文件用于测试系统调用 pwritev 在使用
4+ // 大量小块数据写入时的性能表现。
5+ // 重点测试 user_access_len() 函数的开销
6+ //
7+ // ==============================================
8+
9+ #include <fcntl.h>
10+ #include <stdio.h>
11+ #include <stdlib.h>
12+ #include <sys/stat.h>
13+ #include <sys/types.h>
14+ #include <sys/uio.h>
15+ #include <unistd.h>
16+ #include <time.h>
17+ #include <string.h>
18+ #include <errno.h>
19+
20+ #define TEST_FILE "pwritev_test.dat"
21+ #define NUM_IOV 1000
22+ #define SMALL_DATA_SIZE 64
23+ #define TOTAL_ITERATIONS 100
24+
25+ // 测试用的小数据块
26+ struct test_iovec {
27+ struct iovec iov [NUM_IOV ];
28+ char data [NUM_IOV ][SMALL_DATA_SIZE ];
29+ };
30+
31+ // 初始化测试数据
32+ void init_test_data (struct test_iovec * test_vec ) {
33+ for (int i = 0 ; i < NUM_IOV ; i ++ ) {
34+ // 填充每个小块数据
35+ snprintf (test_vec -> data [i ], SMALL_DATA_SIZE , "Block_%04d:abcdefghijklmnopqrstuvwxyz" , i );
36+ test_vec -> iov [i ].iov_base = test_vec -> data [i ];
37+ test_vec -> iov [i ].iov_len = strlen (test_vec -> data [i ]);
38+ }
39+ }
40+
41+ // 性能测试函数
42+ double test_pwritev_performance (int fd , struct test_iovec * test_vec , int iterations ) {
43+ struct timespec start , end ;
44+
45+ clock_gettime (CLOCK_MONOTONIC , & start );
46+
47+ for (int i = 0 ; i < iterations ; i ++ ) {
48+ ssize_t written = pwritev (fd , test_vec -> iov , NUM_IOV , 0 );
49+ if (written == -1 ) {
50+ perror ("pwritev failed" );
51+ exit (EXIT_FAILURE );
52+ }
53+
54+ // 计算总写入字节数
55+ size_t total_bytes = 0 ;
56+ for (int j = 0 ; j < NUM_IOV ; j ++ ) {
57+ total_bytes += test_vec -> iov [j ].iov_len ;
58+ }
59+
60+ if (written != total_bytes ) {
61+ fprintf (stderr , "Partial write: expected %zu, got %zd\n" , total_bytes , written );
62+ exit (EXIT_FAILURE );
63+ }
64+ }
65+
66+ clock_gettime (CLOCK_MONOTONIC , & end );
67+
68+ double elapsed = (end .tv_sec - start .tv_sec ) +
69+ (end .tv_nsec - start .tv_nsec ) / 1e9 ;
70+
71+ return elapsed ;
72+ }
73+
74+ // 对比测试:使用单独的 write 调用
75+ double test_individual_writes_performance (int fd , struct test_iovec * test_vec , int iterations ) {
76+ struct timespec start , end ;
77+
78+ clock_gettime (CLOCK_MONOTONIC , & start );
79+
80+ for (int i = 0 ; i < iterations ; i ++ ) {
81+ off_t offset = 0 ;
82+ for (int j = 0 ; j < NUM_IOV ; j ++ ) {
83+ ssize_t written = pwrite (fd , test_vec -> iov [j ].iov_base ,
84+ test_vec -> iov [j ].iov_len , offset );
85+ if (written == -1 ) {
86+ perror ("pwrite failed" );
87+ exit (EXIT_FAILURE );
88+ }
89+ if (written != test_vec -> iov [j ].iov_len ) {
90+ fprintf (stderr , "Partial write in individual test\n" );
91+ exit (EXIT_FAILURE );
92+ }
93+ offset += written ;
94+ }
95+ }
96+
97+ clock_gettime (CLOCK_MONOTONIC , & end );
98+
99+ double elapsed = (end .tv_sec - start .tv_sec ) +
100+ (end .tv_nsec - start .tv_nsec ) / 1e9 ;
101+
102+ return elapsed ;
103+ }
104+
105+
106+ int main (void ) {
107+ struct test_iovec test_vec ;
108+
109+ printf ("=== pwritev Performance Test ===\n" );
110+ printf ("IOV count: %d\n" , NUM_IOV );
111+ printf ("Small data size: %d bytes\n" , SMALL_DATA_SIZE );
112+ printf ("Iterations: %d\n" , TOTAL_ITERATIONS );
113+ printf ("\n" );
114+
115+ // 初始化测试数据
116+ init_test_data (& test_vec );
117+
118+ // 计算总数据大小
119+ size_t total_data_size = 0 ;
120+ for (int i = 0 ; i < NUM_IOV ; i ++ ) {
121+ total_data_size += test_vec .iov [i ].iov_len ;
122+ }
123+ printf ("Total data per pwritev call: %zu bytes\n" , total_data_size );
124+ printf ("Total data to write: %zu KB\n" ,
125+ (total_data_size * TOTAL_ITERATIONS ) / 1024 );
126+ printf ("\n" );
127+
128+ // 创建测试文件
129+ int fd = open (TEST_FILE , O_CREAT | O_WRONLY | O_TRUNC , 0644 );
130+ if (fd == -1 ) {
131+ perror ("Failed to create test file" );
132+ exit (EXIT_FAILURE );
133+ }
134+
135+ // 性能测试:pwritev
136+ printf ("Testing pwritev performance...\n" );
137+ double pwritev_time = test_pwritev_performance (fd , & test_vec , TOTAL_ITERATIONS );
138+ printf ("pwritev total time: %.4f seconds\n" , pwritev_time );
139+ printf ("pwritev average time per call: %.6f ms\n" ,
140+ (pwritev_time * 1000 ) / TOTAL_ITERATIONS );
141+ printf ("pwritev throughput: %.2f MB/s\n" ,
142+ (total_data_size * TOTAL_ITERATIONS ) / (pwritev_time * 1024 * 1024 ));
143+ printf ("\n" );
144+
145+ // 重置文件位置
146+ if (ftruncate (fd , 0 ) == -1 ) {
147+ perror ("Failed to truncate file" );
148+ close (fd );
149+ exit (EXIT_FAILURE );
150+ }
151+
152+ // 性能测试:单独的 write 调用对比
153+ printf ("Testing individual pwrite performance (baseline)...\n" );
154+ double individual_time = test_individual_writes_performance (fd , & test_vec , TOTAL_ITERATIONS );
155+ printf ("Individual pwrite total time: %.4f seconds\n" , individual_time );
156+ printf ("Individual pwrite average time per call: %.6f ms\n" ,
157+ (individual_time * 1000 ) / TOTAL_ITERATIONS );
158+ printf ("Individual pwrite throughput: %.2f MB/s\n" ,
159+ (total_data_size * TOTAL_ITERATIONS ) / (individual_time * 1024 * 1024 ));
160+ printf ("\n" );
161+
162+ // 性能对比
163+ double speedup = individual_time / pwritev_time ;
164+ printf ("Performance comparison:\n" );
165+ printf ("pwritev is %.2fx faster than individual writes\n" , speedup );
166+ printf ("pwritev saves %.2f%% time\n" , (1 - pwritev_time / individual_time ) * 100 );
167+ printf ("\n" );
168+
169+ // 清理
170+ close (fd );
171+ if (unlink (TEST_FILE ) == -1 ) {
172+ perror ("Failed to remove test file" );
173+ }
174+
175+ printf ("test_pwritev_perf ok\n" );
176+ return 0 ;
177+ }
0 commit comments