6 static char const* rev =
"$Revision: 1.12 $$Date: 2016/11/04 13:57:24 $";
27 #include <boost/thread.hpp>
30 #define TRACE_NAME "malloc_free"
34 # define TRACE( vv, ... ) do { if (opt_v>=vv) printf( __VA_ARGS__ ); } while (0)
43 -V print version and exit\n\
45 --seed= random number generator seed\n\
46 --min= min alloc default=%u\n\
47 --max= max alloc default=%u\n\
49 --new use new/delete\n\
52 --threads= default=%u\n\
53 ", basename(argv[0]), basename(argv[0]), opt_min_alloc, opt_max_alloc\
54 , opt_ave, opt_threads
57 uint64_t opt_hi_water = 1 * (1024 * 1024 * 1024);
58 uint64_t opt_lo_water = 8 * (1024 * 1024);
59 unsigned opt_seed = 2, opt_seed_ = 1;
60 unsigned opt_max_alloc = 128 * (1024);
61 unsigned opt_min_alloc = 1 * (1024);
64 unsigned opt_ave = 100000;
69 uint64_t human(
char* optarg)
72 uint64_t ret = strtoul(optarg, &endptr, 0);
73 if (*endptr ==
'k' || *endptr ==
'K') ret *= 1024;
74 else if (*endptr ==
'm' || *endptr ==
'M') ret *= 1024 * 1024;
75 else if (*endptr ==
'g' || *endptr ==
'G') ret *= 1024 * 1024 * 1024;
79 void parse_args(
int argc,
char* argv[])
85 static struct option long_options[] = {
87 {
"help", no_argument, 0,
'h'},
88 {
"seed", required_argument,0,
's'},
89 {
"hi", required_argument,0, 1},
90 {
"lo", required_argument,0, 2},
91 {
"min", required_argument,0, 3},
92 {
"max", required_argument,0, 4},
93 {
"no-seed", no_argument, 0, 5},
94 {
"ave", required_argument,0, 6},
95 {
"threads", required_argument,0, 7},
96 {
"time", required_argument,0,
't'},
99 opt = getopt_long(argc, argv,
"?hvVs:t:",
101 if (opt == -1)
break;
104 case '?':
case 'h': printf(USAGE);
107 case 'V': printf(
"%s\n", rev);
112 case 's': opt_seed = strtoul(optarg, NULL, 0);
115 case 't': opt_time = strtoul(optarg, NULL, 0);
117 case 1: opt_hi_water = human(optarg);
119 case 2: opt_lo_water = human(optarg);
121 case 3: opt_min_alloc = human(optarg);
123 case 4: opt_max_alloc = human(optarg);
125 case 5: opt_seed_ = 0;
127 case 6: opt_ave = strtoul(optarg, NULL, 0);
129 case 7: opt_threads = strtoul(optarg, NULL, 0);
132 printf(
"?? getopt returned character code 0%o ??\n", opt);
138 uint64_t gettimeofday_us(
void)
141 gettimeofday(&tv, NULL);
142 return (uint64_t)tv.tv_sec * 1000000 + tv.tv_usec;
149 , running_tot_MB_(0.0) {}
151 void update(
double MB, int64_t delta_us)
154 running_tot_MB_ += MB;
155 running_tot_us_ += delta_us;
156 times_per_.insert((
double)delta_us / MB);
161 void get_clear(
double& tot_MB, int64_t& tot_delta_us,
int& count,
double& min,
double& max)
164 tot_MB = running_tot_MB_;
165 tot_delta_us = running_tot_us_;
171 min = *(times_per_.begin());
172 max = *(times_per_.rbegin());
174 running_tot_MB_ = 0.0;
183 int64_t running_tot_us_;
184 double running_tot_MB_;
185 std::multiset<double> times_per_;
191 Malloc(Stats& stats) : num_mallocs(0)
194 #define NUM_DELTAS opt_ave
200 void* malloc_opt(
unsigned* alloc_bytes)
203 if ((opt_max_alloc - opt_min_alloc) == 0)
204 *alloc_bytes = opt_max_alloc;
206 *alloc_bytes = (random() % (opt_max_alloc - opt_min_alloc)) + opt_min_alloc;
208 uint64_t t0 = gettimeofday_us();
210 ptr = malloc(*alloc_bytes);
217 for (ii = 0; ii < *alloc_bytes; ii += 4096)
218 *(
int*)((
char*)ptr + ii) = 1;
221 int64_t delta_us = gettimeofday_us() - t0;
222 # define PAGES_PER_MB (0x100000/4096.0)
223 double MB = (double)ii / PAGES_PER_MB;
224 TLOG(TLVL_INFO) <<
"malloc " << *alloc_bytes <<
" bytes ii=" << ii <<
" MB=" << MB;
226 stats_.update(MB, delta_us);
233 static void do_work(Stats* stats)
235 std::vector<std::pair<void*, int>> ptr_vec;
236 uint64_t total_allocation = 0;
246 while (total_allocation < opt_lo_water)
248 unsigned alloc_bytes;
249 int8_t* ptr = (int8_t*)mm.malloc_opt(&alloc_bytes);
250 std::pair<void*, int> xx(ptr, alloc_bytes);
251 ptr_vec.push_back(xx);
252 total_allocation += alloc_bytes;
256 printf(
"ptr_vec.size()=%ld\n", ptr_vec.size());
261 long tst = (direction == up) ? RAND_MAX / 4 * 3 : RAND_MAX / 4 * 1;
262 int malloc1_or_free0 = rr < tst;
264 if (malloc1_or_free0)
267 unsigned alloc_bytes;
268 int8_t* ptr = (int8_t*)mm.malloc_opt(&alloc_bytes);
269 std::pair<void*, int> xx(ptr, alloc_bytes);
270 ptr_vec.push_back(xx);
271 total_allocation += alloc_bytes;
272 if (total_allocation > opt_hi_water && direction == up)
274 TLOG(TLVL_WARNING) <<
"direction=down";
281 if (ptr_vec.size() == 0)
283 printf(
"size()==0 - continue\n");
286 unsigned free_idx = random() % ptr_vec.size();
287 std::pair<void*, int> xx(ptr_vec[free_idx]);
288 ptr_vec.erase(ptr_vec.begin() + free_idx);
289 TLOG(TLVL_INFO) <<
"free " << xx.second;
291 total_allocation -= xx.second;
292 if (total_allocation < opt_lo_water && direction == down)
294 TLOG(TLVL_WARNING) <<
"direction=up";
301 void send_metrics(
double ave,
double min,
double max,
double MBperalloc, int32_t threads)
303 # define CLUSTER "mu2e DAQ"
304 # define GROUP "memory"
305 send_gmetric(
"ave", std::to_string(ave).c_str(),
"double",
"us/MB"
306 ,
"both", 30, 0, GROUP, CLUSTER,
"ave us/MB",
"ave us/MB");
307 send_gmetric(
"min", std::to_string(min).c_str(),
"double",
"us/MB"
308 ,
"both", 30, 0, GROUP, CLUSTER,
"min us/MB",
"min us/MB");
309 send_gmetric(
"max", std::to_string(max).c_str(),
"double",
"us/MB"
310 ,
"both", 30, 0, GROUP, CLUSTER,
"max us/MB",
"max us/MB");
311 send_gmetric(
"ave_MBperalloc", std::to_string(MBperalloc).c_str(),
"double",
"MB/alloc"
312 ,
"both", 30, 0, GROUP, CLUSTER,
"ave MB/alloc",
"ave MB/alloc");
313 send_gmetric(
"threads", std::to_string(threads).c_str(),
"int32",
"threads"
314 ,
"both", 30, 0, GROUP, CLUSTER,
"threads mallocing",
"threads mallocing");
318 int main(
int argc,
char* argv[])
320 parse_args(argc, argv);
322 if (opt_hi_water < opt_lo_water)
324 printf(
"changeing hi_water from %lu to %lu\n", opt_hi_water, opt_lo_water);
325 opt_hi_water = opt_lo_water;
327 if (opt_max_alloc < opt_min_alloc)
329 printf(
"changeing max_alloc from %u to %u\n", opt_max_alloc, opt_min_alloc);
330 opt_max_alloc = opt_min_alloc;
334 opt_seed = time(NULL) * getpid();
336 printf(
"opt_seed=%u RAND_MAX=%d random=%ld\n", opt_seed, RAND_MAX, random());
337 printf(
"lo=%ld hi=%ld min=%u max=%u\n", opt_lo_water, opt_hi_water, opt_min_alloc, opt_max_alloc);
342 std::vector<boost::thread> threads(opt_threads);
343 std::vector<Stats> stats(opt_threads);
344 for (
auto nn = 0; nn < opt_threads; ++nn)
346 boost::thread::attributes attrs;
347 attrs.set_stack_size(4096 * 2000);
349 threads[nn] = boost::thread(attrs, boost::bind(&do_work, &stats[nn]));
351 catch (
const boost::exception& e)
353 TLOG(TLVL_ERROR) <<
"Caught boost::exception starting malloc_free thread: " << boost::diagnostic_information(e) <<
", errno=" << errno;
354 std::cerr <<
"Caught boost::exception starting malloc_free thread: " << boost::diagnostic_information(e) <<
", errno=" << errno << std::endl;
359 uint64_t start_us = gettimeofday_us();
360 uint64_t end_us = start_us + (1000000L * opt_time);
362 for (
auto cc = 2; 1; ++cc)
364 double tot_ave_usperMB = 0.0, tot_min_usperMB = 0.0, tot_max_usperMB = 0.0, tot_MBperalloc = 0.0;
365 for (
auto nn = 0; nn < opt_threads; ++nn)
371 stats[nn].get_clear(tot_MB, tot_us, count, min, max);
372 tot_ave_usperMB += (tot_MB != 0.0) ? (double)tot_us / tot_MB : 0.0;
373 tot_min_usperMB += min;
374 tot_max_usperMB += max;
375 tot_MBperalloc += count ? tot_MB / count : 0;
376 TLOG(6) <<
"nn=" << nn <<
" tot_MB=" << std::setprecision(4) << tot_MB <<
" count=" << count;
379 TLOG(TLVL_ERROR) << opt_threads
380 <<
" ave malloc usec/MB ave=" << std::setprecision(4) << tot_ave_usperMB / opt_threads
381 <<
" min=" << tot_min_usperMB / opt_threads <<
" max=" << tot_max_usperMB / opt_threads
382 <<
" MB/alloc=" << std::setprecision(1) << tot_MBperalloc / opt_threads;
384 send_metrics(tot_ave_usperMB / opt_threads
385 , tot_min_usperMB / opt_threads
386 , tot_max_usperMB / opt_threads
387 , tot_MBperalloc / opt_threads
390 uint64_t target = start_us + (5000000L * cc);
391 uint64_t now_us = gettimeofday_us();
392 int64_t slp = target - now_us;
395 if ((now_us + slp) > end_us)
401 printf(
"cc=%d slp=%ld\n", cc, slp);
406 send_metrics(0.0, 0.0, 0.0, 0.0, 0);
408 for (
auto nn = 0; nn < opt_threads; ++nn)
412 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.