selftests: vm: add COW time test for KSM pages
Since merged pages are copied every time they need to be modified, the write access time is different between shared and non-shared pages. Add ksm_cow_time() function which evaluates latency of these COW breaks. First, 4000 pages are allocated and the time, required to modify 1 byte in every other page, is measured. After this, the pages are merged into 2000 pairs and in each pair, 1 page is modified (i.e. they are decoupled) to detect COW breaks. The time needed to break COW of merged pages is then compared with performance of non-shared pages. The test is run as follows: ./ksm_tests -C The output: Total size: 15 MiB Not merged pages: Total time: 0.002185489 s Average speed: 3202.945 MiB/s Merged pages: Total time: 0.004386872 s Average speed: 1595.670 MiB/s Link: https://lkml.kernel.org/r/1d03ee0d1b341959d4b61672c6401d498bff5652.1629386192.git.zhansayabagdaulet@gmail.com Signed-off-by: Zhansaya Bagdauletkyzy <zhansayabagdaulet@gmail.com> Reviewed-by: Tyler Hicks <tyhicks@linux.microsoft.com> Reviewed-by: Pavel Tatashin <pasha.tatashin@soleen.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
9e7cb94ca2
commit
924a11bd16
|
@ -33,7 +33,8 @@ enum ksm_test_name {
|
|||
CHECK_KSM_UNMERGE,
|
||||
CHECK_KSM_ZERO_PAGE_MERGE,
|
||||
CHECK_KSM_NUMA_MERGE,
|
||||
KSM_MERGE_TIME
|
||||
KSM_MERGE_TIME,
|
||||
KSM_COW_TIME
|
||||
};
|
||||
|
||||
static int ksm_write_sysfs(const char *file_path, unsigned long val)
|
||||
|
@ -98,7 +99,8 @@ static void print_help(void)
|
|||
" -U (page unmerging)\n"
|
||||
" -P evaluate merging time and speed.\n"
|
||||
" For this test, the size of duplicated memory area (in MiB)\n"
|
||||
" must be provided using -s option\n\n");
|
||||
" must be provided using -s option\n"
|
||||
" -C evaluate the time required to break COW of merged pages.\n\n");
|
||||
|
||||
printf(" -a: specify the access protections of pages.\n"
|
||||
" <prot> must be of the form [rwx].\n"
|
||||
|
@ -455,6 +457,77 @@ static int ksm_merge_time(int mapping, int prot, int timeout, size_t map_size)
|
|||
return KSFT_FAIL;
|
||||
}
|
||||
|
||||
static int ksm_cow_time(int mapping, int prot, int timeout, size_t page_size)
|
||||
{
|
||||
void *map_ptr;
|
||||
struct timespec start_time, end_time;
|
||||
unsigned long cow_time_ns;
|
||||
|
||||
/* page_count must be less than 2*page_size */
|
||||
size_t page_count = 4000;
|
||||
|
||||
map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count);
|
||||
if (!map_ptr)
|
||||
return KSFT_FAIL;
|
||||
|
||||
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
|
||||
perror("clock_gettime");
|
||||
return KSFT_FAIL;
|
||||
}
|
||||
for (size_t i = 0; i < page_count - 1; i = i + 2)
|
||||
memset(map_ptr + page_size * i, '-', 1);
|
||||
if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
|
||||
perror("clock_gettime");
|
||||
return KSFT_FAIL;
|
||||
}
|
||||
|
||||
cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
|
||||
(end_time.tv_nsec - start_time.tv_nsec);
|
||||
|
||||
printf("Total size: %lu MiB\n\n", (page_size * page_count) / MB);
|
||||
printf("Not merged pages:\n");
|
||||
printf("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC,
|
||||
cow_time_ns % NSEC_PER_SEC);
|
||||
printf("Average speed: %.3f MiB/s\n\n", ((page_size * (page_count / 2)) / MB) /
|
||||
((double)cow_time_ns / NSEC_PER_SEC));
|
||||
|
||||
/* Create 2000 pairs of duplicate pages */
|
||||
for (size_t i = 0; i < page_count - 1; i = i + 2) {
|
||||
memset(map_ptr + page_size * i, '+', i / 2 + 1);
|
||||
memset(map_ptr + page_size * (i + 1), '+', i / 2 + 1);
|
||||
}
|
||||
if (ksm_merge_pages(map_ptr, page_size * page_count, start_time, timeout))
|
||||
goto err_out;
|
||||
|
||||
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
|
||||
perror("clock_gettime");
|
||||
goto err_out;
|
||||
}
|
||||
for (size_t i = 0; i < page_count - 1; i = i + 2)
|
||||
memset(map_ptr + page_size * i, '-', 1);
|
||||
if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
|
||||
perror("clock_gettime");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
|
||||
(end_time.tv_nsec - start_time.tv_nsec);
|
||||
|
||||
printf("Merged pages:\n");
|
||||
printf("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC,
|
||||
cow_time_ns % NSEC_PER_SEC);
|
||||
printf("Average speed: %.3f MiB/s\n", ((page_size * (page_count / 2)) / MB) /
|
||||
((double)cow_time_ns / NSEC_PER_SEC));
|
||||
|
||||
munmap(map_ptr, page_size * page_count);
|
||||
return KSFT_PASS;
|
||||
|
||||
err_out:
|
||||
printf("Not OK\n");
|
||||
munmap(map_ptr, page_size * page_count);
|
||||
return KSFT_FAIL;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret, opt;
|
||||
|
@ -468,7 +541,7 @@ int main(int argc, char *argv[])
|
|||
bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT;
|
||||
long size_MB = 0;
|
||||
|
||||
while ((opt = getopt(argc, argv, "ha:p:l:z:m:s:MUZNP")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "ha:p:l:z:m:s:MUZNPC")) != -1) {
|
||||
switch (opt) {
|
||||
case 'a':
|
||||
prot = str_to_prot(optarg);
|
||||
|
@ -522,6 +595,9 @@ int main(int argc, char *argv[])
|
|||
case 'P':
|
||||
test_name = KSM_MERGE_TIME;
|
||||
break;
|
||||
case 'C':
|
||||
test_name = KSM_COW_TIME;
|
||||
break;
|
||||
default:
|
||||
return KSFT_FAIL;
|
||||
}
|
||||
|
@ -571,6 +647,10 @@ int main(int argc, char *argv[])
|
|||
ret = ksm_merge_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
|
||||
size_MB);
|
||||
break;
|
||||
case KSM_COW_TIME:
|
||||
ret = ksm_cow_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
|
||||
page_size);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ksm_restore(&ksm_sysfs_old)) {
|
||||
|
|
Loading…
Reference in New Issue