All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] add KSM performance tests
@ 2021-08-01 15:43 Zhansaya Bagdauletkyzy
  2021-08-01 15:43 ` [PATCH 1/2] selftests: vm: add KSM merging time test Zhansaya Bagdauletkyzy
  2021-08-01 15:43 ` [PATCH 2/2] selftests: vm: add COW time test for KSM pages Zhansaya Bagdauletkyzy
  0 siblings, 2 replies; 7+ messages in thread
From: Zhansaya Bagdauletkyzy @ 2021-08-01 15:43 UTC (permalink / raw)
  To: akpm, shuah
  Cc: linux-mm, linux-kselftest, linux-kernel, tyhicks, pasha.tatashin

Extend KSM self tests with a performance benchmark. These tests are not
part of regular regression testing, as they are mainly intended to be
used by developers making changes to the memory management subsystem. 
Both patches were developed against linux-next.

Zhansaya Bagdauletkyzy (2):
  selftests: vm: add KSM merging time test
  selftests: vm: add COW time test for KSM pages

 tools/testing/selftests/vm/ksm_tests.c | 136 ++++++++++++++++++++++++-
 1 file changed, 132 insertions(+), 4 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/2] selftests: vm: add KSM merging time test
  2021-08-01 15:43 [PATCH 0/2] add KSM performance tests Zhansaya Bagdauletkyzy
@ 2021-08-01 15:43 ` Zhansaya Bagdauletkyzy
  2021-08-04  3:33     ` Pavel Tatashin
  2021-08-01 15:43 ` [PATCH 2/2] selftests: vm: add COW time test for KSM pages Zhansaya Bagdauletkyzy
  1 sibling, 1 reply; 7+ messages in thread
From: Zhansaya Bagdauletkyzy @ 2021-08-01 15:43 UTC (permalink / raw)
  To: akpm, shuah
  Cc: linux-mm, linux-kselftest, linux-kernel, tyhicks, pasha.tatashin

Add ksm_merge_time() function to determine speed and time needed for
merging. The total spent time is shown in seconds while speed is
in MB/s. User must specify the size of duplicated memory area (in MB)
before running the test.

The test is run as follows: ./ksm_tests -P -s 100
The output:
        Total size:    100 MB
        Total time:    0.309561 s
        Average speed: 323 MB/s

Signed-off-by: Zhansaya Bagdauletkyzy <zhansayabagdaulet@gmail.com>
---
 tools/testing/selftests/vm/ksm_tests.c | 76 ++++++++++++++++++++++++--
 1 file changed, 72 insertions(+), 4 deletions(-)

diff --git a/tools/testing/selftests/vm/ksm_tests.c b/tools/testing/selftests/vm/ksm_tests.c
index cdeb4a028538..91c6ff496655 100644
--- a/tools/testing/selftests/vm/ksm_tests.c
+++ b/tools/testing/selftests/vm/ksm_tests.c
@@ -7,6 +7,7 @@
 #include <numa.h>
 
 #include "../kselftest.h"
+#include "../../../../include/vdso/time64.h"
 
 #define KSM_SYSFS_PATH "/sys/kernel/mm/ksm/"
 #define KSM_FP(s) (KSM_SYSFS_PATH s)
@@ -15,6 +16,7 @@
 #define KSM_PROT_STR_DEFAULT "rw"
 #define KSM_USE_ZERO_PAGES_DEFAULT false
 #define KSM_MERGE_ACROSS_NODES_DEFAULT true
+#define MB_TO_B 1000000ul
 
 struct ksm_sysfs {
 	unsigned long max_page_sharing;
@@ -30,7 +32,8 @@ enum ksm_test_name {
 	CHECK_KSM_MERGE,
 	CHECK_KSM_UNMERGE,
 	CHECK_KSM_ZERO_PAGE_MERGE,
-	CHECK_KSM_NUMA_MERGE
+	CHECK_KSM_NUMA_MERGE,
+	KSM_MERGE_TIME
 };
 
 static int ksm_write_sysfs(const char *file_path, unsigned long val)
@@ -86,13 +89,16 @@ static int str_to_prot(char *prot_str)
 static void print_help(void)
 {
 	printf("usage: ksm_tests [-h] <test type> [-a prot] [-p page_count] [-l timeout]\n"
-	       "[-z use_zero_pages] [-m merge_across_nodes]\n");
+	       "[-z use_zero_pages] [-m merge_across_nodes] [-s size]\n");
 
 	printf("Supported <test type>:\n"
 	       " -M (page merging)\n"
 	       " -Z (zero pages merging)\n"
 	       " -N (merging of pages in different NUMA nodes)\n"
-	       " -U (page unmerging)\n\n");
+	       " -U (page unmerging)\n"
+	       " -P evaluate merging time and speed.\n"
+	       "    For this test, the size of duplicated memory area (in MB)\n"
+	       "    must be provided using -s option\n\n");
 
 	printf(" -a: specify the access protections of pages.\n"
 	       "     <prot> must be of the form [rwx].\n"
@@ -105,6 +111,7 @@ static void print_help(void)
 	       "     Default: %d\n", KSM_USE_ZERO_PAGES_DEFAULT);
 	printf(" -m: change merge_across_nodes tunable\n"
 	       "     Default: %d\n", KSM_MERGE_ACROSS_NODES_DEFAULT);
+	printf(" -s: the size of duplicated memory area (in MB)\n");
 
 	exit(0);
 }
@@ -407,6 +414,49 @@ static int check_ksm_numa_merge(int mapping, int prot, int timeout, bool merge_a
 	return KSFT_FAIL;
 }
 
+static int ksm_merge_time(int mapping, int prot, int timeout, size_t map_size)
+{
+	void *map_ptr;
+	struct timespec start_time, end_time;
+	long scan_time_s, scan_time_ns;
+	int avg_speed;
+
+	map_size *= MB_TO_B;
+
+	map_ptr = allocate_memory(NULL, prot, mapping, '*', map_size);
+	if (!map_ptr)
+		return KSFT_FAIL;
+
+	if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
+		perror("clock_gettime");
+		goto err_out;
+	}
+	if (ksm_merge_pages(map_ptr, map_size, start_time, timeout))
+		goto err_out;
+	if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
+		perror("clock_gettime");
+		goto err_out;
+	}
+
+	scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
+		       (end_time.tv_nsec - start_time.tv_nsec);
+	scan_time_s = scan_time_ns / NSEC_PER_SEC;
+	scan_time_ns %= NSEC_PER_SEC;
+	avg_speed = map_size / (scan_time_s * USEC_PER_SEC + scan_time_ns / NSEC_PER_USEC);
+
+	printf("Total size:    %lu MB\n", map_size / MB_TO_B);
+	printf("Total time:    %ld.%06ld s\n", scan_time_s, scan_time_ns / NSEC_PER_USEC);
+	printf("Average speed: %d MB/s\n", avg_speed);
+
+	munmap(map_ptr, map_size);
+	return KSFT_PASS;
+
+err_out:
+	printf("Not OK\n");
+	munmap(map_ptr, map_size);
+	return KSFT_FAIL;
+}
+
 int main(int argc, char *argv[])
 {
 	int ret, opt;
@@ -418,8 +468,9 @@ int main(int argc, char *argv[])
 	int test_name = CHECK_KSM_MERGE;
 	bool use_zero_pages = KSM_USE_ZERO_PAGES_DEFAULT;
 	bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT;
+	long size_MB = 0;
 
-	while ((opt = getopt(argc, argv, "ha:p:l:z:m:MUZN")) != -1) {
+	while ((opt = getopt(argc, argv, "ha:p:l:z:m:s:MUZNP")) != -1) {
 		switch (opt) {
 		case 'a':
 			prot = str_to_prot(optarg);
@@ -453,6 +504,12 @@ int main(int argc, char *argv[])
 			else
 				merge_across_nodes = 1;
 			break;
+		case 's':
+			size_MB = atoi(optarg);
+			if (size_MB <= 0) {
+				printf("Size must be greater than 0\n");
+				return KSFT_FAIL;
+			}
 		case 'M':
 			break;
 		case 'U':
@@ -464,6 +521,9 @@ int main(int argc, char *argv[])
 		case 'N':
 			test_name = CHECK_KSM_NUMA_MERGE;
 			break;
+		case 'P':
+			test_name = KSM_MERGE_TIME;
+			break;
 		default:
 			return KSFT_FAIL;
 		}
@@ -505,6 +565,14 @@ int main(int argc, char *argv[])
 		ret = check_ksm_numa_merge(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
 					   merge_across_nodes, page_size);
 		break;
+	case KSM_MERGE_TIME:
+		if (size_MB == 0) {
+			printf("Option '-s' is required.\n");
+			return KSFT_FAIL;
+		}
+		ret = ksm_merge_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
+				     size_MB);
+		break;
 	}
 
 	if (ksm_restore(&ksm_sysfs_old)) {
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/2] selftests: vm: add COW time test for KSM pages
  2021-08-01 15:43 [PATCH 0/2] add KSM performance tests Zhansaya Bagdauletkyzy
  2021-08-01 15:43 ` [PATCH 1/2] selftests: vm: add KSM merging time test Zhansaya Bagdauletkyzy
@ 2021-08-01 15:43 ` Zhansaya Bagdauletkyzy
  2021-08-04  3:41     ` Pavel Tatashin
  1 sibling, 1 reply; 7+ messages in thread
From: Zhansaya Bagdauletkyzy @ 2021-08-01 15:43 UTC (permalink / raw)
  To: akpm, shuah
  Cc: linux-mm, linux-kselftest, linux-kernel, tyhicks, pasha.tatashin

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, duplicated pages are merged and then the time required
to write to each of the pages is detected.

The test is run as follows: ./ksm_tests -C -p 5000
The output:
	Total COW time:      0.012612 s
	The number of pages: 5000
	Average speed:	     1623 MB/s

Signed-off-by: Zhansaya Bagdauletkyzy <zhansayabagdaulet@gmail.com>
---
 tools/testing/selftests/vm/ksm_tests.c | 66 ++++++++++++++++++++++++--
 1 file changed, 63 insertions(+), 3 deletions(-)

diff --git a/tools/testing/selftests/vm/ksm_tests.c b/tools/testing/selftests/vm/ksm_tests.c
index 91c6ff496655..3ffd899b2bd9 100644
--- a/tools/testing/selftests/vm/ksm_tests.c
+++ b/tools/testing/selftests/vm/ksm_tests.c
@@ -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,9 @@ 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 MB)\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"
+	       "    The number of pages can be defined using -p option.\n\n");
 
 	printf(" -a: specify the access protections of pages.\n"
 	       "     <prot> must be of the form [rwx].\n"
@@ -457,6 +460,56 @@ 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, long page_count)
+{
+	void *map_ptr;
+	struct timespec start_time, end_time;
+	long cow_time_s, cow_time_ns;
+	int avg_speed;
+
+	if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
+		perror("clock_gettime");
+		return KSFT_FAIL;
+	}
+
+	map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count);
+	if (!map_ptr)
+		return KSFT_FAIL;
+
+	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; i++)
+		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);
+	cow_time_s = cow_time_ns / NSEC_PER_SEC;
+	cow_time_ns %= NSEC_PER_SEC;
+	avg_speed = (page_size * page_count) / (cow_time_s * USEC_PER_SEC +
+						cow_time_ns / NSEC_PER_USEC);
+
+	printf("Total COW time:      %ld.%06ld s\n", cow_time_s, cow_time_ns / NSEC_PER_USEC);
+	printf("The number of pages: %ld\n", page_count);
+	printf("Average speed:	     %d MB/s\n", avg_speed);
+
+	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;
@@ -470,7 +523,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);
@@ -524,6 +577,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;
 		}
@@ -573,6 +629,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, page_count);
+		break;
 	}
 
 	if (ksm_restore(&ksm_sysfs_old)) {
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] selftests: vm: add KSM merging time test
  2021-08-01 15:43 ` [PATCH 1/2] selftests: vm: add KSM merging time test Zhansaya Bagdauletkyzy
@ 2021-08-04  3:33     ` Pavel Tatashin
  0 siblings, 0 replies; 7+ messages in thread
From: Pavel Tatashin @ 2021-08-04  3:33 UTC (permalink / raw)
  To: Zhansaya Bagdauletkyzy
  Cc: Andrew Morton, shuah, linux-mm, linux-kselftest, LKML, Tyler Hicks

On Sun, Aug 1, 2021 at 11:43 AM Zhansaya Bagdauletkyzy
<zhansayabagdaulet@gmail.com> wrote:
>
> Add ksm_merge_time() function to determine speed and time needed for
> merging. The total spent time is shown in seconds while speed is
> in MB/s. User must specify the size of duplicated memory area (in MB)
> before running the test.
>
> The test is run as follows: ./ksm_tests -P -s 100
> The output:
>         Total size:    100 MB
>         Total time:    0.309561 s
>         Average speed: 323 MB/s
>
> Signed-off-by: Zhansaya Bagdauletkyzy <zhansayabagdaulet@gmail.com>
> ---
>  tools/testing/selftests/vm/ksm_tests.c | 76 ++++++++++++++++++++++++--
>  1 file changed, 72 insertions(+), 4 deletions(-)
>
> diff --git a/tools/testing/selftests/vm/ksm_tests.c b/tools/testing/selftests/vm/ksm_tests.c
> index cdeb4a028538..91c6ff496655 100644
> --- a/tools/testing/selftests/vm/ksm_tests.c
> +++ b/tools/testing/selftests/vm/ksm_tests.c
> @@ -7,6 +7,7 @@
>  #include <numa.h>
>
>  #include "../kselftest.h"
> +#include "../../../../include/vdso/time64.h"
>
>  #define KSM_SYSFS_PATH "/sys/kernel/mm/ksm/"
>  #define KSM_FP(s) (KSM_SYSFS_PATH s)
> @@ -15,6 +16,7 @@
>  #define KSM_PROT_STR_DEFAULT "rw"
>  #define KSM_USE_ZERO_PAGES_DEFAULT false
>  #define KSM_MERGE_ACROSS_NODES_DEFAULT true
> +#define MB_TO_B 1000000ul

We should calculate everything in true megabytes.

#define MB (1ul << 20)

Also, print in MiB/s.

>
>  struct ksm_sysfs {
>         unsigned long max_page_sharing;
> @@ -30,7 +32,8 @@ enum ksm_test_name {
>         CHECK_KSM_MERGE,
>         CHECK_KSM_UNMERGE,
>         CHECK_KSM_ZERO_PAGE_MERGE,
> -       CHECK_KSM_NUMA_MERGE
> +       CHECK_KSM_NUMA_MERGE,
> +       KSM_MERGE_TIME
>  };
>
>  static int ksm_write_sysfs(const char *file_path, unsigned long val)
> @@ -86,13 +89,16 @@ static int str_to_prot(char *prot_str)
>  static void print_help(void)
>  {
>         printf("usage: ksm_tests [-h] <test type> [-a prot] [-p page_count] [-l timeout]\n"
> -              "[-z use_zero_pages] [-m merge_across_nodes]\n");
> +              "[-z use_zero_pages] [-m merge_across_nodes] [-s size]\n");
>
>         printf("Supported <test type>:\n"
>                " -M (page merging)\n"
>                " -Z (zero pages merging)\n"
>                " -N (merging of pages in different NUMA nodes)\n"
> -              " -U (page unmerging)\n\n");
> +              " -U (page unmerging)\n"
> +              " -P evaluate merging time and speed.\n"
> +              "    For this test, the size of duplicated memory area (in MB)\n"
> +              "    must be provided using -s option\n\n");
>
>         printf(" -a: specify the access protections of pages.\n"
>                "     <prot> must be of the form [rwx].\n"
> @@ -105,6 +111,7 @@ static void print_help(void)
>                "     Default: %d\n", KSM_USE_ZERO_PAGES_DEFAULT);
>         printf(" -m: change merge_across_nodes tunable\n"
>                "     Default: %d\n", KSM_MERGE_ACROSS_NODES_DEFAULT);
> +       printf(" -s: the size of duplicated memory area (in MB)\n");
>
>         exit(0);
>  }
> @@ -407,6 +414,49 @@ static int check_ksm_numa_merge(int mapping, int prot, int timeout, bool merge_a
>         return KSFT_FAIL;
>  }
>
> +static int ksm_merge_time(int mapping, int prot, int timeout, size_t map_size)
> +{
> +       void *map_ptr;
> +       struct timespec start_time, end_time;
> +       long scan_time_s, scan_time_ns;

Should be "unsigned long".

> +       int avg_speed;
No needed, see below.

> +
> +       map_size *= MB_TO_B;
> +
> +       map_ptr = allocate_memory(NULL, prot, mapping, '*', map_size);
> +       if (!map_ptr)
> +               return KSFT_FAIL;
> +
> +       if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
> +               perror("clock_gettime");
> +               goto err_out;
> +       }
> +       if (ksm_merge_pages(map_ptr, map_size, start_time, timeout))
> +               goto err_out;
> +       if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
> +               perror("clock_gettime");
> +               goto err_out;
> +       }
> +
> +       scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
> +                      (end_time.tv_nsec - start_time.tv_nsec);

> +       scan_time_s = scan_time_ns / NSEC_PER_SEC;
> +       scan_time_ns %= NSEC_PER_SEC;
> +       avg_speed = map_size / (scan_time_s * USEC_PER_SEC + scan_time_ns / NSEC_PER_USEC);

Please remove the above three lines, and replace with bellow suggestions:

> +
> +       printf("Total size:    %lu MB\n", map_size / MB_TO_B);

printf("Total size:    %lu MiB\n", map_size / MB);

> +       printf("Total time:    %ld.%06ld s\n", scan_time_s, scan_time_ns / NSEC_PER_USEC);

printf("Total time:    %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC,
scan_time_ns % NSEC_PER_SEC));

> +       printf("Average speed: %d MB/s\n", avg_speed);

The following would give a much better precision:
printf("Average speed:  %.3f MiB/s\n", (map_size / MB) /
((double)scan_time_ns / NSEC_PER_SEC));

> +
> +       munmap(map_ptr, map_size);
> +       return KSFT_PASS;
> +
> +err_out:
> +       printf("Not OK\n");
> +       munmap(map_ptr, map_size);
> +       return KSFT_FAIL;
> +}
> +
>  int main(int argc, char *argv[])
>  {
>         int ret, opt;
> @@ -418,8 +468,9 @@ int main(int argc, char *argv[])
>         int test_name = CHECK_KSM_MERGE;
>         bool use_zero_pages = KSM_USE_ZERO_PAGES_DEFAULT;
>         bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT;
> +       long size_MB = 0;
>
> -       while ((opt = getopt(argc, argv, "ha:p:l:z:m:MUZN")) != -1) {
> +       while ((opt = getopt(argc, argv, "ha:p:l:z:m:s:MUZNP")) != -1) {
>                 switch (opt) {
>                 case 'a':
>                         prot = str_to_prot(optarg);
> @@ -453,6 +504,12 @@ int main(int argc, char *argv[])
>                         else
>                                 merge_across_nodes = 1;
>                         break;
> +               case 's':
> +                       size_MB = atoi(optarg);
> +                       if (size_MB <= 0) {
> +                               printf("Size must be greater than 0\n");
> +                               return KSFT_FAIL;
> +                       }
>                 case 'M':
>                         break;
>                 case 'U':
> @@ -464,6 +521,9 @@ int main(int argc, char *argv[])
>                 case 'N':
>                         test_name = CHECK_KSM_NUMA_MERGE;
>                         break;
> +               case 'P':
> +                       test_name = KSM_MERGE_TIME;
> +                       break;
>                 default:
>                         return KSFT_FAIL;
>                 }
> @@ -505,6 +565,14 @@ int main(int argc, char *argv[])
>                 ret = check_ksm_numa_merge(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
>                                            merge_across_nodes, page_size);
>                 break;
> +       case KSM_MERGE_TIME:
> +               if (size_MB == 0) {
> +                       printf("Option '-s' is required.\n");
> +                       return KSFT_FAIL;
> +               }
> +               ret = ksm_merge_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
> +                                    size_MB);
> +               break;
>         }
>
>         if (ksm_restore(&ksm_sysfs_old)) {
> --
> 2.25.1
>

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] selftests: vm: add KSM merging time test
@ 2021-08-04  3:33     ` Pavel Tatashin
  0 siblings, 0 replies; 7+ messages in thread
From: Pavel Tatashin @ 2021-08-04  3:33 UTC (permalink / raw)
  To: Zhansaya Bagdauletkyzy
  Cc: Andrew Morton, shuah, linux-mm, linux-kselftest, LKML, Tyler Hicks

On Sun, Aug 1, 2021 at 11:43 AM Zhansaya Bagdauletkyzy
<zhansayabagdaulet@gmail.com> wrote:
>
> Add ksm_merge_time() function to determine speed and time needed for
> merging. The total spent time is shown in seconds while speed is
> in MB/s. User must specify the size of duplicated memory area (in MB)
> before running the test.
>
> The test is run as follows: ./ksm_tests -P -s 100
> The output:
>         Total size:    100 MB
>         Total time:    0.309561 s
>         Average speed: 323 MB/s
>
> Signed-off-by: Zhansaya Bagdauletkyzy <zhansayabagdaulet@gmail.com>
> ---
>  tools/testing/selftests/vm/ksm_tests.c | 76 ++++++++++++++++++++++++--
>  1 file changed, 72 insertions(+), 4 deletions(-)
>
> diff --git a/tools/testing/selftests/vm/ksm_tests.c b/tools/testing/selftests/vm/ksm_tests.c
> index cdeb4a028538..91c6ff496655 100644
> --- a/tools/testing/selftests/vm/ksm_tests.c
> +++ b/tools/testing/selftests/vm/ksm_tests.c
> @@ -7,6 +7,7 @@
>  #include <numa.h>
>
>  #include "../kselftest.h"
> +#include "../../../../include/vdso/time64.h"
>
>  #define KSM_SYSFS_PATH "/sys/kernel/mm/ksm/"
>  #define KSM_FP(s) (KSM_SYSFS_PATH s)
> @@ -15,6 +16,7 @@
>  #define KSM_PROT_STR_DEFAULT "rw"
>  #define KSM_USE_ZERO_PAGES_DEFAULT false
>  #define KSM_MERGE_ACROSS_NODES_DEFAULT true
> +#define MB_TO_B 1000000ul

We should calculate everything in true megabytes.

#define MB (1ul << 20)

Also, print in MiB/s.

>
>  struct ksm_sysfs {
>         unsigned long max_page_sharing;
> @@ -30,7 +32,8 @@ enum ksm_test_name {
>         CHECK_KSM_MERGE,
>         CHECK_KSM_UNMERGE,
>         CHECK_KSM_ZERO_PAGE_MERGE,
> -       CHECK_KSM_NUMA_MERGE
> +       CHECK_KSM_NUMA_MERGE,
> +       KSM_MERGE_TIME
>  };
>
>  static int ksm_write_sysfs(const char *file_path, unsigned long val)
> @@ -86,13 +89,16 @@ static int str_to_prot(char *prot_str)
>  static void print_help(void)
>  {
>         printf("usage: ksm_tests [-h] <test type> [-a prot] [-p page_count] [-l timeout]\n"
> -              "[-z use_zero_pages] [-m merge_across_nodes]\n");
> +              "[-z use_zero_pages] [-m merge_across_nodes] [-s size]\n");
>
>         printf("Supported <test type>:\n"
>                " -M (page merging)\n"
>                " -Z (zero pages merging)\n"
>                " -N (merging of pages in different NUMA nodes)\n"
> -              " -U (page unmerging)\n\n");
> +              " -U (page unmerging)\n"
> +              " -P evaluate merging time and speed.\n"
> +              "    For this test, the size of duplicated memory area (in MB)\n"
> +              "    must be provided using -s option\n\n");
>
>         printf(" -a: specify the access protections of pages.\n"
>                "     <prot> must be of the form [rwx].\n"
> @@ -105,6 +111,7 @@ static void print_help(void)
>                "     Default: %d\n", KSM_USE_ZERO_PAGES_DEFAULT);
>         printf(" -m: change merge_across_nodes tunable\n"
>                "     Default: %d\n", KSM_MERGE_ACROSS_NODES_DEFAULT);
> +       printf(" -s: the size of duplicated memory area (in MB)\n");
>
>         exit(0);
>  }
> @@ -407,6 +414,49 @@ static int check_ksm_numa_merge(int mapping, int prot, int timeout, bool merge_a
>         return KSFT_FAIL;
>  }
>
> +static int ksm_merge_time(int mapping, int prot, int timeout, size_t map_size)
> +{
> +       void *map_ptr;
> +       struct timespec start_time, end_time;
> +       long scan_time_s, scan_time_ns;

Should be "unsigned long".

> +       int avg_speed;
No needed, see below.

> +
> +       map_size *= MB_TO_B;
> +
> +       map_ptr = allocate_memory(NULL, prot, mapping, '*', map_size);
> +       if (!map_ptr)
> +               return KSFT_FAIL;
> +
> +       if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
> +               perror("clock_gettime");
> +               goto err_out;
> +       }
> +       if (ksm_merge_pages(map_ptr, map_size, start_time, timeout))
> +               goto err_out;
> +       if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
> +               perror("clock_gettime");
> +               goto err_out;
> +       }
> +
> +       scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
> +                      (end_time.tv_nsec - start_time.tv_nsec);

> +       scan_time_s = scan_time_ns / NSEC_PER_SEC;
> +       scan_time_ns %= NSEC_PER_SEC;
> +       avg_speed = map_size / (scan_time_s * USEC_PER_SEC + scan_time_ns / NSEC_PER_USEC);

Please remove the above three lines, and replace with bellow suggestions:

> +
> +       printf("Total size:    %lu MB\n", map_size / MB_TO_B);

printf("Total size:    %lu MiB\n", map_size / MB);

> +       printf("Total time:    %ld.%06ld s\n", scan_time_s, scan_time_ns / NSEC_PER_USEC);

printf("Total time:    %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC,
scan_time_ns % NSEC_PER_SEC));

> +       printf("Average speed: %d MB/s\n", avg_speed);

The following would give a much better precision:
printf("Average speed:  %.3f MiB/s\n", (map_size / MB) /
((double)scan_time_ns / NSEC_PER_SEC));

> +
> +       munmap(map_ptr, map_size);
> +       return KSFT_PASS;
> +
> +err_out:
> +       printf("Not OK\n");
> +       munmap(map_ptr, map_size);
> +       return KSFT_FAIL;
> +}
> +
>  int main(int argc, char *argv[])
>  {
>         int ret, opt;
> @@ -418,8 +468,9 @@ int main(int argc, char *argv[])
>         int test_name = CHECK_KSM_MERGE;
>         bool use_zero_pages = KSM_USE_ZERO_PAGES_DEFAULT;
>         bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT;
> +       long size_MB = 0;
>
> -       while ((opt = getopt(argc, argv, "ha:p:l:z:m:MUZN")) != -1) {
> +       while ((opt = getopt(argc, argv, "ha:p:l:z:m:s:MUZNP")) != -1) {
>                 switch (opt) {
>                 case 'a':
>                         prot = str_to_prot(optarg);
> @@ -453,6 +504,12 @@ int main(int argc, char *argv[])
>                         else
>                                 merge_across_nodes = 1;
>                         break;
> +               case 's':
> +                       size_MB = atoi(optarg);
> +                       if (size_MB <= 0) {
> +                               printf("Size must be greater than 0\n");
> +                               return KSFT_FAIL;
> +                       }
>                 case 'M':
>                         break;
>                 case 'U':
> @@ -464,6 +521,9 @@ int main(int argc, char *argv[])
>                 case 'N':
>                         test_name = CHECK_KSM_NUMA_MERGE;
>                         break;
> +               case 'P':
> +                       test_name = KSM_MERGE_TIME;
> +                       break;
>                 default:
>                         return KSFT_FAIL;
>                 }
> @@ -505,6 +565,14 @@ int main(int argc, char *argv[])
>                 ret = check_ksm_numa_merge(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
>                                            merge_across_nodes, page_size);
>                 break;
> +       case KSM_MERGE_TIME:
> +               if (size_MB == 0) {
> +                       printf("Option '-s' is required.\n");
> +                       return KSFT_FAIL;
> +               }
> +               ret = ksm_merge_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
> +                                    size_MB);
> +               break;
>         }
>
>         if (ksm_restore(&ksm_sysfs_old)) {
> --
> 2.25.1
>


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 2/2] selftests: vm: add COW time test for KSM pages
  2021-08-01 15:43 ` [PATCH 2/2] selftests: vm: add COW time test for KSM pages Zhansaya Bagdauletkyzy
@ 2021-08-04  3:41     ` Pavel Tatashin
  0 siblings, 0 replies; 7+ messages in thread
From: Pavel Tatashin @ 2021-08-04  3:41 UTC (permalink / raw)
  To: Zhansaya Bagdauletkyzy
  Cc: Andrew Morton, shuah, linux-mm, linux-kselftest, LKML, Tyler Hicks

On Sun, Aug 1, 2021 at 11:43 AM Zhansaya Bagdauletkyzy
<zhansayabagdaulet@gmail.com> wrote:
>
> 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, duplicated pages are merged and then the time required
> to write to each of the pages is detected.

Hi Zhansaya,

This test would make more sense if we also had a baseline on how long
it takes to modify unmerged pages. This way it would show the true
cost of having a write on a previously merged page vs. if it was never
merged.

Also, instead of having all pages merged together, I'd rather see a
collection of merged pairs of pages, which are decoupled as one of the
pages gets modified: i.e.
Page1 - Page2: merged
Page3 - Page4: merged
...

then, go through pages: Page2, Page4, ... and modify the first byte in
each of them and measure the time it takes. Do the same when these
pages are not merged, and print both numbers so the cost of KSM and
COW can be calculated.

Thanks,
Pasha

>
> The test is run as follows: ./ksm_tests -C -p 5000
> The output:
>         Total COW time:      0.012612 s
>         The number of pages: 5000
>         Average speed:       1623 MB/s
>
> Signed-off-by: Zhansaya Bagdauletkyzy <zhansayabagdaulet@gmail.com>
> ---
>  tools/testing/selftests/vm/ksm_tests.c | 66 ++++++++++++++++++++++++--
>  1 file changed, 63 insertions(+), 3 deletions(-)
>
> diff --git a/tools/testing/selftests/vm/ksm_tests.c b/tools/testing/selftests/vm/ksm_tests.c
> index 91c6ff496655..3ffd899b2bd9 100644
> --- a/tools/testing/selftests/vm/ksm_tests.c
> +++ b/tools/testing/selftests/vm/ksm_tests.c
> @@ -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,9 @@ 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 MB)\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"
> +              "    The number of pages can be defined using -p option.\n\n");
>
>         printf(" -a: specify the access protections of pages.\n"
>                "     <prot> must be of the form [rwx].\n"
> @@ -457,6 +460,56 @@ 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, long page_count)
> +{
> +       void *map_ptr;
> +       struct timespec start_time, end_time;
> +       long cow_time_s, cow_time_ns;
> +       int avg_speed;
> +
> +       if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
> +               perror("clock_gettime");
> +               return KSFT_FAIL;
> +       }
> +
> +       map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count);
> +       if (!map_ptr)
> +               return KSFT_FAIL;
> +
> +       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; i++)
> +               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);
> +       cow_time_s = cow_time_ns / NSEC_PER_SEC;
> +       cow_time_ns %= NSEC_PER_SEC;
> +       avg_speed = (page_size * page_count) / (cow_time_s * USEC_PER_SEC +
> +                                               cow_time_ns / NSEC_PER_USEC);
> +
> +       printf("Total COW time:      %ld.%06ld s\n", cow_time_s, cow_time_ns / NSEC_PER_USEC);
> +       printf("The number of pages: %ld\n", page_count);
> +       printf("Average speed:       %d MB/s\n", avg_speed);
> +
> +       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;
> @@ -470,7 +523,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);
> @@ -524,6 +577,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;
>                 }
> @@ -573,6 +629,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, page_count);
> +               break;
>         }
>
>         if (ksm_restore(&ksm_sysfs_old)) {
> --
> 2.25.1
>

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 2/2] selftests: vm: add COW time test for KSM pages
@ 2021-08-04  3:41     ` Pavel Tatashin
  0 siblings, 0 replies; 7+ messages in thread
From: Pavel Tatashin @ 2021-08-04  3:41 UTC (permalink / raw)
  To: Zhansaya Bagdauletkyzy
  Cc: Andrew Morton, shuah, linux-mm, linux-kselftest, LKML, Tyler Hicks

On Sun, Aug 1, 2021 at 11:43 AM Zhansaya Bagdauletkyzy
<zhansayabagdaulet@gmail.com> wrote:
>
> 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, duplicated pages are merged and then the time required
> to write to each of the pages is detected.

Hi Zhansaya,

This test would make more sense if we also had a baseline on how long
it takes to modify unmerged pages. This way it would show the true
cost of having a write on a previously merged page vs. if it was never
merged.

Also, instead of having all pages merged together, I'd rather see a
collection of merged pairs of pages, which are decoupled as one of the
pages gets modified: i.e.
Page1 - Page2: merged
Page3 - Page4: merged
...

then, go through pages: Page2, Page4, ... and modify the first byte in
each of them and measure the time it takes. Do the same when these
pages are not merged, and print both numbers so the cost of KSM and
COW can be calculated.

Thanks,
Pasha

>
> The test is run as follows: ./ksm_tests -C -p 5000
> The output:
>         Total COW time:      0.012612 s
>         The number of pages: 5000
>         Average speed:       1623 MB/s
>
> Signed-off-by: Zhansaya Bagdauletkyzy <zhansayabagdaulet@gmail.com>
> ---
>  tools/testing/selftests/vm/ksm_tests.c | 66 ++++++++++++++++++++++++--
>  1 file changed, 63 insertions(+), 3 deletions(-)
>
> diff --git a/tools/testing/selftests/vm/ksm_tests.c b/tools/testing/selftests/vm/ksm_tests.c
> index 91c6ff496655..3ffd899b2bd9 100644
> --- a/tools/testing/selftests/vm/ksm_tests.c
> +++ b/tools/testing/selftests/vm/ksm_tests.c
> @@ -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,9 @@ 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 MB)\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"
> +              "    The number of pages can be defined using -p option.\n\n");
>
>         printf(" -a: specify the access protections of pages.\n"
>                "     <prot> must be of the form [rwx].\n"
> @@ -457,6 +460,56 @@ 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, long page_count)
> +{
> +       void *map_ptr;
> +       struct timespec start_time, end_time;
> +       long cow_time_s, cow_time_ns;
> +       int avg_speed;
> +
> +       if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
> +               perror("clock_gettime");
> +               return KSFT_FAIL;
> +       }
> +
> +       map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count);
> +       if (!map_ptr)
> +               return KSFT_FAIL;
> +
> +       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; i++)
> +               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);
> +       cow_time_s = cow_time_ns / NSEC_PER_SEC;
> +       cow_time_ns %= NSEC_PER_SEC;
> +       avg_speed = (page_size * page_count) / (cow_time_s * USEC_PER_SEC +
> +                                               cow_time_ns / NSEC_PER_USEC);
> +
> +       printf("Total COW time:      %ld.%06ld s\n", cow_time_s, cow_time_ns / NSEC_PER_USEC);
> +       printf("The number of pages: %ld\n", page_count);
> +       printf("Average speed:       %d MB/s\n", avg_speed);
> +
> +       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;
> @@ -470,7 +523,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);
> @@ -524,6 +577,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;
>                 }
> @@ -573,6 +629,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, page_count);
> +               break;
>         }
>
>         if (ksm_restore(&ksm_sysfs_old)) {
> --
> 2.25.1
>


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2021-08-04  3:42 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-01 15:43 [PATCH 0/2] add KSM performance tests Zhansaya Bagdauletkyzy
2021-08-01 15:43 ` [PATCH 1/2] selftests: vm: add KSM merging time test Zhansaya Bagdauletkyzy
2021-08-04  3:33   ` Pavel Tatashin
2021-08-04  3:33     ` Pavel Tatashin
2021-08-01 15:43 ` [PATCH 2/2] selftests: vm: add COW time test for KSM pages Zhansaya Bagdauletkyzy
2021-08-04  3:41   ` Pavel Tatashin
2021-08-04  3:41     ` Pavel Tatashin

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.