Raw
1 #define USE_THE_REPOSITORY_VARIABLE
2
3 #include "test-tool.h"
4 #include "environment.h"
5 #include "name-hash.h"
6 #include "parse-options.h"
7 #include "read-cache-ll.h"
8 #include "repository.h"
9 #include "setup.h"
10 #include "trace.h"
11
12 static int single;
13 static int multi;
14 static int count = 1;
15 static int dump;
16 static int perf;
17 static int analyze;
18 static int analyze_step;
19
20 /*
21 * Dump the contents of the "dir" and "name" hash tables to stdout.
22 * If you sort the result, you can compare it with the other type
23 * mode and verify that both single and multi produce the same set.
24 */
25 static void dump_run(void)
26 {
27 struct hashmap_iter iter_dir;
28 struct hashmap_iter iter_cache;
29
30 /* Stolen from name-hash.c */
31 struct dir_entry {
32 struct hashmap_entry ent;
33 struct dir_entry *parent;
34 int nr;
35 unsigned int namelen;
36 char name[FLEX_ARRAY];
37 };
38
39 struct dir_entry *dir;
40 struct cache_entry *ce;
41
42 repo_read_index(the_repository);
43 if (single) {
44 test_lazy_init_name_hash(the_repository->index, 0);
45 } else {
46 int nr_threads_used = test_lazy_init_name_hash(the_repository->index, 1);
47 if (!nr_threads_used)
48 die("non-threaded code path used");
49 }
50
51 hashmap_for_each_entry(&the_repository->index->dir_hash, &iter_dir, dir,
52 ent /* member name */)
53 printf("dir %08x %7d %s\n", dir->ent.hash, dir->nr, dir->name);
54
55 hashmap_for_each_entry(&the_repository->index->name_hash, &iter_cache, ce,
56 ent /* member name */)
57 printf("name %08x %s\n", ce->ent.hash, ce->name);
58
59 discard_index(the_repository->index);
60 }
61
62 /*
63 * Run the single or multi threaded version "count" times and
64 * report on the time taken.
65 */
66 static uint64_t time_runs(int try_threaded)
67 {
68 uint64_t t0, t1, t2;
69 uint64_t sum = 0;
70 uint64_t avg;
71 int nr_threads_used;
72 int i;
73
74 for (i = 0; i < count; i++) {
75 t0 = getnanotime();
76 repo_read_index(the_repository);
77 t1 = getnanotime();
78 nr_threads_used = test_lazy_init_name_hash(the_repository->index, try_threaded);
79 t2 = getnanotime();
80
81 sum += (t2 - t1);
82
83 if (try_threaded && !nr_threads_used)
84 die("non-threaded code path used");
85
86 if (nr_threads_used)
87 printf("%f %f %d multi %d\n",
88 ((double)(t1 - t0))/1000000000,
89 ((double)(t2 - t1))/1000000000,
90 the_repository->index->cache_nr,
91 nr_threads_used);
92 else
93 printf("%f %f %d single\n",
94 ((double)(t1 - t0))/1000000000,
95 ((double)(t2 - t1))/1000000000,
96 the_repository->index->cache_nr);
97 fflush(stdout);
98
99 discard_index(the_repository->index);
100 }
101
102 avg = sum / count;
103 if (count > 1)
104 printf("avg %f %s\n",
105 (double)avg/1000000000,
106 (try_threaded) ? "multi" : "single");
107
108 return avg;
109 }
110
111 /*
112 * Try a series of runs varying the "istate->cache_nr" and
113 * try to find a good value for the multi-threaded criteria.
114 */
115 static void analyze_run(void)
116 {
117 uint64_t t1s, t1m, t2s, t2m;
118 int cache_nr_limit;
119 int nr_threads_used = 0;
120 int i;
121 int nr;
122
123 repo_read_index(the_repository);
124 cache_nr_limit = the_repository->index->cache_nr;
125 discard_index(the_repository->index);
126
127 nr = analyze;
128 while (1) {
129 uint64_t sum_single = 0;
130 uint64_t sum_multi = 0;
131 uint64_t avg_single;
132 uint64_t avg_multi;
133
134 if (nr > cache_nr_limit)
135 nr = cache_nr_limit;
136
137 for (i = 0; i < count; i++) {
138 repo_read_index(the_repository);
139 the_repository->index->cache_nr = nr; /* cheap truncate of index */
140 t1s = getnanotime();
141 test_lazy_init_name_hash(the_repository->index, 0);
142 t2s = getnanotime();
143 sum_single += (t2s - t1s);
144 the_repository->index->cache_nr = cache_nr_limit;
145 discard_index(the_repository->index);
146
147 repo_read_index(the_repository);
148 the_repository->index->cache_nr = nr; /* cheap truncate of index */
149 t1m = getnanotime();
150 nr_threads_used = test_lazy_init_name_hash(the_repository->index, 1);
151 t2m = getnanotime();
152 sum_multi += (t2m - t1m);
153 the_repository->index->cache_nr = cache_nr_limit;
154 discard_index(the_repository->index);
155
156 if (!nr_threads_used)
157 printf(" [size %8d] [single %f] non-threaded code path used\n",
158 nr, ((double)(t2s - t1s))/1000000000);
159 else
160 printf(" [size %8d] [single %f] %c [multi %f %d]\n",
161 nr,
162 ((double)(t2s - t1s))/1000000000,
163 (((t2s - t1s) < (t2m - t1m)) ? '<' : '>'),
164 ((double)(t2m - t1m))/1000000000,
165 nr_threads_used);
166 fflush(stdout);
167 }
168 if (count > 1) {
169 avg_single = sum_single / count;
170 avg_multi = sum_multi / count;
171 if (!nr_threads_used)
172 printf("avg [size %8d] [single %f]\n",
173 nr,
174 (double)avg_single/1000000000);
175 else
176 printf("avg [size %8d] [single %f] %c [multi %f %d]\n",
177 nr,
178 (double)avg_single/1000000000,
179 (avg_single < avg_multi ? '<' : '>'),
180 (double)avg_multi/1000000000,
181 nr_threads_used);
182 fflush(stdout);
183 }
184
185 if (nr >= cache_nr_limit)
186 return;
187 nr += analyze_step;
188 }
189 }
190
191 int cmd__lazy_init_name_hash(int argc, const char **argv)
192 {
193 const char *usage[] = {
194 "test-tool lazy-init-name-hash -d (-s | -m)",
195 "test-tool lazy-init-name-hash -p [-c c]",
196 "test-tool lazy-init-name-hash -a a [--step s] [-c c]",
197 "test-tool lazy-init-name-hash (-s | -m) [-c c]",
198 "test-tool lazy-init-name-hash -s -m [-c c]",
199 NULL
200 };
201 struct option options[] = {
202 OPT_BOOL('s', "single", &single, "run single-threaded code"),
203 OPT_BOOL('m', "multi", &multi, "run multi-threaded code"),
204 OPT_INTEGER('c', "count", &count, "number of passes"),
205 OPT_BOOL('d', "dump", &dump, "dump hash tables"),
206 OPT_BOOL('p', "perf", &perf, "compare single vs multi"),
207 OPT_INTEGER('a', "analyze", &analyze, "analyze different multi sizes"),
208 OPT_INTEGER(0, "step", &analyze_step, "analyze step factor"),
209 OPT_END(),
210 };
211 const char *prefix;
212 uint64_t avg_single, avg_multi;
213
214 prefix = setup_git_directory(the_repository);
215
216 argc = parse_options(argc, argv, prefix, options, usage, 0);
217
218 /*
219 * istate->dir_hash is only created when ignore_case is set.
220 */
221 repo_config_values(the_repository)->ignore_case = 1;
222
223 if (dump) {
224 if (perf || analyze > 0)
225 die("cannot combine dump, perf, or analyze");
226 if (count > 1)
227 die("count not valid with dump");
228 if (single && multi)
229 die("cannot use both single and multi with dump");
230 if (!single && !multi)
231 die("dump requires either single or multi");
232 dump_run();
233 return 0;
234 }
235
236 if (perf) {
237 if (analyze > 0)
238 die("cannot combine dump, perf, or analyze");
239 if (single || multi)
240 die("cannot use single or multi with perf");
241 avg_single = time_runs(0);
242 avg_multi = time_runs(1);
243 if (avg_multi > avg_single)
244 die("multi is slower");
245 return 0;
246 }
247
248 if (analyze) {
249 if (analyze < 500)
250 die("analyze must be at least 500");
251 if (!analyze_step)
252 analyze_step = analyze;
253 if (single || multi)
254 die("cannot use single or multi with analyze");
255 analyze_run();
256 return 0;
257 }
258
259 if (!single && !multi)
260 die("require either -s or -m or both");
261
262 if (single)
263 time_runs(0);
264 if (multi)
265 time_runs(1);
266
267 return 0;
268 }