1 /* Benchmark malloc and free functions.
2    Copyright (C) 2019-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <pthread.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <malloc.h>
23 #include <sys/resource.h>
24 #include "bench-timing.h"
25 #include "json-lib.h"
26 
27 /* Benchmark the malloc/free performance of a varying number of blocks of a
28    given size.  This enables performance tracking of the t-cache and fastbins.
29    It tests 3 different scenarios: single-threaded using main arena,
30    multi-threaded using thread-arena, and main arena with SINGLE_THREAD_P
31    false.  */
32 
33 #define NUM_ITERS 200000
34 #define NUM_ALLOCS 4
35 #define MAX_ALLOCS 1600
36 
37 typedef struct
38 {
39   size_t iters;
40   size_t size;
41   int n;
42   timing_t elapsed;
43 } malloc_args;
44 
45 static void
do_benchmark(malloc_args * args,int ** arr)46 do_benchmark (malloc_args *args, int **arr)
47 {
48   timing_t start, stop;
49   size_t iters = args->iters;
50   size_t size = args->size;
51   int n = args->n;
52 
53   TIMING_NOW (start);
54 
55   for (int j = 0; j < iters; j++)
56     {
57       for (int i = 0; i < n; i++)
58 	arr[i] = malloc (size);
59 
60       for (int i = 0; i < n; i++)
61 	free (arr[i]);
62     }
63 
64   TIMING_NOW (stop);
65 
66   TIMING_DIFF (args->elapsed, start, stop);
67 }
68 
69 static malloc_args tests[3][NUM_ALLOCS];
70 static int allocs[NUM_ALLOCS] = { 25, 100, 400, MAX_ALLOCS };
71 
72 static void *
thread_test(void * p)73 thread_test (void *p)
74 {
75   int **arr = (int**)p;
76 
77   /* Run benchmark multi-threaded.  */
78   for (int i = 0; i < NUM_ALLOCS; i++)
79     do_benchmark (&tests[2][i], arr);
80 
81   return p;
82 }
83 
84 void
bench(unsigned long size)85 bench (unsigned long size)
86 {
87   size_t iters = NUM_ITERS;
88   int **arr = (int**) malloc (MAX_ALLOCS * sizeof (void*));
89 
90   for (int t = 0; t < 3; t++)
91     for (int i = 0; i < NUM_ALLOCS; i++)
92       {
93 	tests[t][i].n = allocs[i];
94 	tests[t][i].size = size;
95 	tests[t][i].iters = iters / allocs[i];
96 
97 	/* Do a quick warmup run.  */
98 	if (t == 0)
99 	  do_benchmark (&tests[0][i], arr);
100       }
101 
102   /* Run benchmark single threaded in main_arena.  */
103   for (int i = 0; i < NUM_ALLOCS; i++)
104     do_benchmark (&tests[0][i], arr);
105 
106   /* Run benchmark in a thread_arena.  */
107   pthread_t t;
108   pthread_create (&t, NULL, thread_test, (void*)arr);
109   pthread_join (t, NULL);
110 
111   /* Repeat benchmark in main_arena with SINGLE_THREAD_P == false.  */
112   for (int i = 0; i < NUM_ALLOCS; i++)
113     do_benchmark (&tests[1][i], arr);
114 
115   free (arr);
116 
117   json_ctx_t json_ctx;
118 
119   json_init (&json_ctx, 0, stdout);
120 
121   json_document_begin (&json_ctx);
122 
123   json_attr_string (&json_ctx, "timing_type", TIMING_TYPE);
124 
125   json_attr_object_begin (&json_ctx, "functions");
126 
127   json_attr_object_begin (&json_ctx, "malloc");
128 
129   char s[100];
130   double iters2 = iters;
131 
132   json_attr_object_begin (&json_ctx, "");
133   json_attr_double (&json_ctx, "malloc_block_size", size);
134 
135   struct rusage usage;
136   getrusage (RUSAGE_SELF, &usage);
137   json_attr_double (&json_ctx, "max_rss", usage.ru_maxrss);
138 
139   for (int i = 0; i < NUM_ALLOCS; i++)
140     {
141       sprintf (s, "main_arena_st_allocs_%04d_time", allocs[i]);
142       json_attr_double (&json_ctx, s, tests[0][i].elapsed / iters2);
143     }
144 
145   for (int i = 0; i < NUM_ALLOCS; i++)
146     {
147       sprintf (s, "main_arena_mt_allocs_%04d_time", allocs[i]);
148       json_attr_double (&json_ctx, s, tests[1][i].elapsed / iters2);
149     }
150 
151   for (int i = 0; i < NUM_ALLOCS; i++)
152     {
153       sprintf (s, "thread_arena__allocs_%04d_time", allocs[i]);
154       json_attr_double (&json_ctx, s, tests[2][i].elapsed / iters2);
155     }
156 
157   json_attr_object_end (&json_ctx);
158 
159   json_attr_object_end (&json_ctx);
160 
161   json_attr_object_end (&json_ctx);
162 
163   json_document_end (&json_ctx);
164 }
165 
usage(const char * name)166 static void usage (const char *name)
167 {
168   fprintf (stderr, "%s: <alloc_size>\n", name);
169   exit (1);
170 }
171 
172 int
main(int argc,char ** argv)173 main (int argc, char **argv)
174 {
175   long val = 16;
176   if (argc == 2)
177     val = strtol (argv[1], NULL, 0);
178 
179   if (argc > 2 || val <= 0)
180     usage (argv[0]);
181 
182   bench (val);
183 
184   return 0;
185 }
186