6 static char const* rev =
"$Revision: 1.12 $$Date: 2016/11/04 13:57:24 $";
27 #include <boost/thread.hpp>
33 # define TRACE( vv, ... ) do { if (opt_v>=vv) printf( __VA_ARGS__ ); } while (0)
42 -V print version and exit\n\
44 --seed= random number generator seed\n\
45 --min= min alloc default=%u\n\
46 --max= max alloc default=%u\n\
48 --new use new/delete\n\
51 --threads= default=%u\n\
52 ", basename(argv[0]), basename(argv[0]), opt_min_alloc, opt_max_alloc\
53 , opt_ave, opt_threads
56 uint64_t opt_hi_water = 1 * (1024 * 1024 * 1024);
57 uint64_t opt_lo_water = 8 * (1024 * 1024);
58 unsigned opt_seed = 2, opt_seed_ = 1;
59 unsigned opt_max_alloc = 128 * (1024);
60 unsigned opt_min_alloc = 1 * (1024);
63 unsigned opt_ave = 100000;
68 uint64_t human(
char* optarg)
71 uint64_t ret = strtoul(optarg, &endptr, 0);
72 if (*endptr ==
'k' || *endptr ==
'K') ret *= 1024;
73 else if (*endptr ==
'm' || *endptr ==
'M') ret *= 1024 * 1024;
74 else if (*endptr ==
'g' || *endptr ==
'G') ret *= 1024 * 1024 * 1024;
78 void parse_args(
int argc,
char* argv[])
84 static struct option long_options[] = {
86 {
"help", no_argument, 0,
'h'},
87 {
"seed", required_argument,0,
's'},
88 {
"hi", required_argument,0, 1},
89 {
"lo", required_argument,0, 2},
90 {
"min", required_argument,0, 3},
91 {
"max", required_argument,0, 4},
92 {
"no-seed", no_argument, 0, 5},
93 {
"ave", required_argument,0, 6},
94 {
"threads", required_argument,0, 7},
95 {
"time", required_argument,0,
't'},
98 opt = getopt_long(argc, argv,
"?hvVs:t:",
100 if (opt == -1)
break;
103 case '?':
case 'h': printf(USAGE);
106 case 'V': printf(
"%s\n", rev);
111 case 's': opt_seed = strtoul(optarg,NULL, 0);
114 case 't': opt_time = strtoul(optarg,NULL, 0);
116 case 1: opt_hi_water = human(optarg);
118 case 2: opt_lo_water = human(optarg);
120 case 3: opt_min_alloc = human(optarg);
122 case 4: opt_max_alloc = human(optarg);
124 case 5: opt_seed_ = 0;
126 case 6: opt_ave = strtoul(optarg,NULL, 0);
128 case 7: opt_threads = strtoul(optarg,NULL, 0);
131 printf(
"?? getopt returned character code 0%o ??\n", opt);
137 uint64_t gettimeofday_us(
void)
140 gettimeofday(&tv, NULL);
141 return (uint64_t)tv.tv_sec * 1000000 + tv.tv_usec;
148 , running_tot_MB_(0.0) {}
150 void update(
double MB, int64_t delta_us)
153 running_tot_MB_ += MB;
154 running_tot_us_ += delta_us;
155 times_per_.insert((
double)delta_us / MB);
160 void get_clear(
double& tot_MB, int64_t& tot_delta_us,
int& count,
double& min,
double& max)
163 tot_MB = running_tot_MB_;
164 tot_delta_us = running_tot_us_;
170 min = *(times_per_.begin());
171 max = *(times_per_.rbegin());
173 running_tot_MB_ = 0.0;
182 int64_t running_tot_us_;
183 double running_tot_MB_;
184 std::multiset<double> times_per_;
193 #define NUM_DELTAS opt_ave
199 void* malloc_opt(
unsigned* alloc_bytes)
202 if ((opt_max_alloc - opt_min_alloc) == 0)
203 *alloc_bytes = opt_max_alloc;
205 *alloc_bytes = (random() % (opt_max_alloc - opt_min_alloc)) + opt_min_alloc;
207 uint64_t t0 = gettimeofday_us();
209 ptr = malloc(*alloc_bytes);
216 for (ii = 0; ii < *alloc_bytes; ii += 4096)
217 *(
int*)((
char*)ptr + ii) = 1;
220 int64_t delta_us = gettimeofday_us() - t0;
221 # define PAGES_PER_MB (0x100000/4096.0)
222 double MB = (double)ii / PAGES_PER_MB;
223 TRACE( 2,
"malloc %u bytes ii=%u MB=%f\n", *alloc_bytes, ii, MB );
225 stats_.update(MB, delta_us);
232 static void do_work(
Stats* stats)
234 std::vector<std::pair<void*, int>> ptr_vec;
235 uint64_t total_allocation = 0;
245 while (total_allocation < opt_lo_water)
247 unsigned alloc_bytes;
248 int8_t* ptr = (int8_t*)mm.malloc_opt(&alloc_bytes);
249 std::pair<void*, int> xx(ptr, alloc_bytes);
250 ptr_vec.push_back(xx);
251 total_allocation += alloc_bytes;
255 printf(
"ptr_vec.size()=%ld\n", ptr_vec.size());
260 long tst = (direction == up) ? RAND_MAX / 4 * 3 : RAND_MAX / 4 * 1;
261 int malloc1_or_free0 = rr < tst;
263 if (malloc1_or_free0)
266 unsigned alloc_bytes;
267 int8_t* ptr = (int8_t*)mm.malloc_opt(&alloc_bytes);
268 std::pair<void*, int> xx(ptr, alloc_bytes);
269 ptr_vec.push_back(xx);
270 total_allocation += alloc_bytes;
271 if (total_allocation > opt_hi_water && direction == up)
273 TRACE( 1,
"direction=down\n" );
280 if (ptr_vec.size() == 0)
282 printf(
"size()==0 - continue\n");
285 unsigned free_idx = random() % ptr_vec.size();
286 std::pair<void*, int> xx(ptr_vec[free_idx]);
287 ptr_vec.erase(ptr_vec.begin() + free_idx);
288 TRACE( 2,
"free %u\n",xx.second);
290 total_allocation -= xx.second;
291 if (total_allocation < opt_lo_water && direction == down)
293 TRACE( 1,
"direction=up\n" );
300 void send_metrics(
double ave,
double min,
double max,
double MBperalloc, int32_t threads)
302 # define CLUSTER "mu2e DAQ"
303 # define GROUP "memory"
304 send_gmetric(
"ave", std::to_string(ave).c_str(),
"double",
"us/MB"
305 ,
"both", 30, 0, GROUP, CLUSTER,
"ave us/MB",
"ave us/MB");
306 send_gmetric(
"min", std::to_string(min).c_str(),
"double",
"us/MB"
307 ,
"both", 30, 0, GROUP, CLUSTER,
"min us/MB",
"min us/MB");
308 send_gmetric(
"max", std::to_string(max).c_str(),
"double",
"us/MB"
309 ,
"both", 30, 0, GROUP, CLUSTER,
"max us/MB",
"max us/MB");
310 send_gmetric(
"ave_MBperalloc", std::to_string(MBperalloc).c_str(),
"double",
"MB/alloc"
311 ,
"both", 30, 0, GROUP, CLUSTER,
"ave MB/alloc",
"ave MB/alloc");
312 send_gmetric(
"threads", std::to_string(threads).c_str(),
"int32",
"threads"
313 ,
"both", 30, 0, GROUP, CLUSTER,
"threads mallocing",
"threads mallocing");
317 int main(
int argc,
char* argv[])
319 parse_args(argc, argv);
321 if (opt_hi_water < opt_lo_water)
323 printf(
"changeing hi_water from %lu to %lu\n", opt_hi_water, opt_lo_water);
324 opt_hi_water = opt_lo_water;
326 if (opt_max_alloc < opt_min_alloc)
328 printf(
"changeing max_alloc from %u to %u\n", opt_max_alloc, opt_min_alloc);
329 opt_max_alloc = opt_min_alloc;
333 opt_seed = time(NULL) * getpid();
335 printf(
"opt_seed=%u RAND_MAX=%d random=%ld\n", opt_seed, RAND_MAX, random());
336 printf(
"lo=%ld hi=%ld min=%u max=%u\n", opt_lo_water, opt_hi_water, opt_min_alloc, opt_max_alloc);
341 std::vector<boost::thread> threads(opt_threads);
342 std::vector<Stats> stats(opt_threads);
343 for (
auto nn = 0; nn < opt_threads; ++nn)
345 boost::thread::attributes attrs;
346 attrs.set_stack_size(4096 * 200);
347 threads[nn] = boost::thread(attrs, boost::bind(&do_work, &stats[nn]));
350 uint64_t start_us = gettimeofday_us();
351 uint64_t end_us = start_us + (1000000L * opt_time);
353 for (
auto cc = 2; 1; ++cc)
355 double tot_ave_usperMB = 0.0, tot_min_usperMB = 0.0, tot_max_usperMB = 0.0, tot_MBperalloc = 0.0;
356 for (
auto nn = 0; nn < opt_threads; ++nn)
362 stats[nn].get_clear(tot_MB, tot_us, count, min, max);
363 tot_ave_usperMB += (tot_MB != 0.0) ? (double)tot_us / tot_MB : 0.0;
364 tot_min_usperMB += min;
365 tot_max_usperMB += max;
366 tot_MBperalloc += count ? tot_MB / count : 0;
367 TRACE( 6,
"nn=%d tot_MB=%.4f count=%d", nn, tot_MB, count );
370 TRACE( 0,
"%d ave malloc usec/MB ave=%.4f min=%.4f max=%.4f MB/alloc=%.1f\n"
371 , opt_threads, tot_ave_usperMB/opt_threads, tot_min_usperMB/opt_threads
372 , tot_max_usperMB/opt_threads, tot_MBperalloc/opt_threads );
374 send_metrics(tot_ave_usperMB / opt_threads
375 , tot_min_usperMB / opt_threads
376 , tot_max_usperMB / opt_threads
377 , tot_MBperalloc / opt_threads
380 uint64_t target = start_us + (5000000L * cc);
381 uint64_t now_us = gettimeofday_us();
382 int64_t slp = target - now_us;
385 if ((now_us + slp) > end_us)
391 printf(
"cc=%d slp=%ld\n", cc, slp);
396 send_metrics(0.0, 0.0, 0.0, 0.0, 0);
398 for (
auto nn = 0; nn < opt_threads; ++nn)
402 printf(
"malloc_free finished.\n");
int init_gmetric(const char *conf)
Initialize Ganglia.
int send_gmetric(const char *name, const char *value, const char *type, const char *units, const char *slope, int tmax, int dmax, const char *group, const char *cluster, const char *desc, const char *title)
Send a metric to gmond.