2 * Eddie Kohler, Yandong Mao, Robert Morris
3 * Copyright (c) 2012-2013 President and Fellows of Harvard College
4 * Copyright (c) 2012-2013 Massachusetts Institute of Technology
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, subject to the conditions
9 * listed in the Masstree LICENSE file. These conditions include: you must
10 * preserve this copyright notice, and you cannot mention the copyright
11 * holders in advertising related to the Software without their permission.
12 * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
13 * notice is a summary of the Masstree LICENSE file; the license in that file
16 #include "perfstat.hh"
17 #include "compiler.hh"
23 enum { MaxCores = 48 }; // Maximum number of cores kvdb statistics support
24 enum { MaxNumaNode = 8 }; // Maximum number of Numa node kvdb statistics support
25 enum { CoresPerChip = MaxCores / MaxNumaNode };
29 #if MEMSTATS && HAVE_NUMA_H && HAVE_LIBNUMA
37 stat::initmain(bool pinthreads) {
40 always_assert(pinthreads && "Using performance counter requires pinning threads to cores!");
42 #if MEMSTATS && HAVE_NUMA_H && HAVE_LIBNUMA
43 if (numa_available() != -1) {
44 always_assert(numa_max_node() <= MaxNumaNode);
45 for (int i = 0; i <= numa_max_node(); i++)
46 numa[i].size = numa_node_size64(i, &numa[i].free);
53 sum_all_cores(const stat **s, int n, const int offset) {
55 for (int i = 0; i < n; i++) {
58 T v = *reinterpret_cast<const T *>(reinterpret_cast<const char *>(s[i]) + offset);
66 sum_one_chip(const stat **s, int n, const int offset, const int chipidx) {
68 for (int i = 0; i < n; i++) {
69 if (!s[i] || s[i]->cid / (MaxCores / MaxNumaNode) != chipidx)
71 T v = *reinterpret_cast<const T *>(reinterpret_cast<const char *>(s[i]) + offset);
79 sum_all_per_chip(const stat **s, int n, const int offset) {
80 kvstats per_chip[MaxNumaNode];
81 for (int i = 0; i < n; i++) {
84 T v = *reinterpret_cast<const T *>(reinterpret_cast<const char *>(s[i]) + offset);
85 per_chip[i / CoresPerChip].add(v);
88 for (int i = 0; i < MaxNumaNode; i++)
89 if (per_chip[i].count)
90 sum.add(per_chip[i].avg());
95 stat::print(const stat **s, int n) {
98 #define sum_all_cores_of(field) \
99 sum_all_cores<typeof(s[0]->field)>(s, n, offsetof(Perf::stat, field))
100 #define sum_one_chip_of(field, c) \
101 sum_one_chip<typeof(s[0]->field)>(s, n, offsetof(Perf::stat, field), c)
102 #define sum_all_per_chip_of(field) \
103 sum_all_per_chip<typeof(s[0]->field)>(s, n, offsetof(Perf::stat, field))
105 #define sum_all_cores_of_array(field, oa) \
106 sum_all_cores<typeof(s[0]->field[0])>(s, n, offsetof(Perf::stat, field) + \
107 sizeof(s[0]->field[0]) * oa)
108 #define sum_one_chip_of_array(field, oa, c) \
109 sum_one_chip<typeof(s[0]->field[0])>(s, n, offsetof(Perf::stat, field) + \
110 sizeof(s[0]->field[0]) * oa, c)
111 #define sum_all_per_chip_of_array(field, oa) \
112 sum_all_per_chip<typeof(s[0]->field[0])>(s, n, offsetof(Perf::stat, field) + \
113 sizeof(s[0]->field[0]) * oa)
116 for (int i = 0; i < n; i++)
117 if (s[i]->ngets < 1000) {
121 kvstats ngets = sum_all_cores_of(ngets);
122 kvstats ntsc = sum_all_cores_of(ntsc);
123 kvstats np = sum_all_cores_of(nprobe);
125 fprintf(stderr, "Total probe %.0f, probe/get %.2f\n", np.sum, np.sum / ngets.sum);
127 fprintf(stderr, "(Inaccurate because PMC is Enabled!)");
129 fprintf(stderr, "Cycles/get (between mark_get_begin and mark_get_end): %.0f\n",
130 ntsc.sum / ngets.sum);
132 for (int i = 0; i < n; i++) {
135 fprintf(stderr, "Core %d:\n", i);
136 for (int pi = 0; pi < 4; pi++) {
137 fprintf(stderr, "\tpmc[%d]: %016" PRIx64 "->%016" PRIx64 "\n",
138 pi, s[i]->pmc_firstget[pi], s[i]->pmc_start[pi]);
139 always_assert(s[i]->pmc_start[pi] >= s[i]->pmc_firstget[pi]);
140 always_assert(s[i]->t1_lastget >= s[i]->t0_firstget);
143 // Compute the start and end time of get phase
144 kvstats getstart = sum_all_cores_of(t0_firstget);
145 kvstats getend = sum_all_cores_of(t1_lastget);
146 getstart.print_report("time of first get");
147 getend.print_report("time of last get");
149 // Compute per-chip pmc during the whole get phase
150 double pcpmc_phase[MaxNumaNode][4];
151 for (int i = 0; i < MaxNumaNode; i++)
152 for (int pi = 0; pi < 4; pi++)
153 pcpmc_phase[i][pi] = sum_one_chip_of_array(pmc_start, pi, i).avg() -
154 sum_one_chip_of_array(pmc_firstget, pi, i).avg();
156 // Compute cputime and realtime during get phase
157 kvstats t_firstget = sum_all_cores_of(t0_firstget);
158 kvstats t_lastget = sum_all_cores_of(t1_lastget);
159 double realtime = t_lastget.avg() - t_firstget.avg();
161 for (int pi = 0; pi < 4; pi++) {
162 fprintf(stderr, "DRAM access to node (pmc %d)\n", pi);
164 for (int i = 0; i < MaxNumaNode; i++) {
165 fprintf(stderr, "\tFrom chip %2d: %8.1f GB/s\n", i,
166 pcpmc_phase[i][pi] * 64 / (realtime * (1 << 30)));
167 sum += pcpmc_phase[i][pi];
169 fprintf(stderr, "\tSum: %8.1f GB/s\n",
170 sum * 64 / (realtime * (1 << 30)));
172 // Print per-get pmc_lookup
173 fprintf(stderr, "Per get statistics (counted between mark_get_begin and mark_get_end):\n");
174 for (int pi = 0; (ngets.sum > 0) && pi < 4; pi ++) {
175 kvstats pmc_lookup = sum_all_cores_of_array(pmc_lookup, pi);
176 kvstats pcpmc_lookup = sum_all_per_chip_of_array(pmc_lookup, pi);
177 fprintf(stderr, "\tpmc%d/get: %6.1f, per_chip_pmc%d/get: %6.1f\n",
178 pi, (double) pmc_lookup.sum / ngets.sum, pi,
179 (double) pcpmc_lookup.sum / ngets.sum);
184 #if MEMSTATS && HAVE_NUMA_H && HAVE_LIBNUMA && 0
185 // collect tree memory
186 kvstats tree_mem = sum_all_cores_of(tree_mem);
187 kvstats tree_keys = sum_all_cores_of(tree_keys);
188 fprintf(stderr, "Memory statistics\n");
189 fprintf(stderr, "\tAllocated per key: %.0f bytes, %.0f\n", tree_mem.sum / tree_keys.sum, tree_keys.sum);
190 if (numa_available() != -1) {
191 unsigned long total_alloc = 0;
192 for (int i = 0; i <= numa_max_node(); i++) {
193 kvstats chip = sum_one_chip_of(tree_mem, i);
195 long long size = numa_node_size64(i, &nowfree);
196 total_alloc += numa[i].free - nowfree;
197 fprintf(stderr, "\tNode %d (MB): size %6lld, allocated = %6lld - "
198 "%6lld = %6lld, tree_mem %6.0f\n",
199 i, size >> 20, numa[i].free >> 20, nowfree >> 20,
200 (numa[i].free - nowfree) / (1 << 20),
201 chip.sum / (1 << 20));
203 fprintf(stderr, "Total allocated memory %ld MB\n", total_alloc >> 20);
208 // collect memory used by epoch based garbage collector
209 kvstats gc_nfree = sum_all_cores_of(gc_nfree);
210 kvstats gc_nalloc = sum_all_cores_of(gc_nalloc);
211 fprintf(stderr, "reuse per gc slot: %.0f, freed: %.0f, allocated: %.0f\n",
212 gc_nfree.sum / gc_nalloc.sum, gc_nfree.sum, gc_nalloc.sum);