00001
00002
00003
00004
00005
00006 static char const* rev = "$Revision: 1.12 $$Date: 2016/11/04 13:57:24 $";
00007
00008
00009
00010
00011
00012
00013 #include <stdio.h>
00014 #include <getopt.h>
00015 #include <stdlib.h>
00016
00017 #include <libgen.h>
00018 #include <time.h>
00019 #include <sys/time.h>
00020 #include <unistd.h>
00021 #include <stdint.h>
00022 #include <string.h>
00023 #include <vector>
00024 #include <tuple>
00025 #include <set>
00026 #include <mutex>
00027 #include <boost/thread.hpp>
00028 #include "send_gmetric.h"
00029
00030 #if 1
00031 # include "trace.h"
00032 #else
00033 # define TRACE( vv, ... ) do { if (opt_v>=vv) printf( __VA_ARGS__ ); } while (0)
00034 #endif
00035
00036 #define USAGE "\
00037 usage: %s [-hv?]\n\
00038 \n\
00039 example: %s\n\
00040 \n\
00041 option:\n\
00042 -V print version and exit\n\
00043 --help\n\
00044 --seed= random number generator seed\n\
00045 --min= min alloc default=%u\n\
00046 --max= max alloc default=%u\n\
00047 -t --time= seconds\n\
00048 --new use new/delete\n\
00049 --no-seed \n\
00050 --ave= default=%d\n\
00051 --threads= default=%u\n\
00052 ", basename(argv[0]), basename(argv[0]), opt_min_alloc, opt_max_alloc\
00053 , opt_ave, opt_threads
00054
00055
00056 uint64_t opt_hi_water = 1 * (1024 * 1024 * 1024);
00057 uint64_t opt_lo_water = 8 * (1024 * 1024);
00058 unsigned opt_seed = 2, opt_seed_ = 1;
00059 unsigned opt_max_alloc = 128 * (1024);
00060 unsigned opt_min_alloc = 1 * (1024);
00061 int opt_v = 0;
00062 int opt_new = 0;
00063 unsigned opt_ave = 100000;
00064 int opt_threads = 1;
00065 int opt_time = 25;
00066 int g_stop = 0;
00067
00068 uint64_t human(char* optarg)
00069 {
00070 char* endptr;
00071 uint64_t ret = strtoul(optarg, &endptr, 0);
00072 if (*endptr == 'k' || *endptr == 'K') ret *= 1024;
00073 else if (*endptr == 'm' || *endptr == 'M') ret *= 1024 * 1024;
00074 else if (*endptr == 'g' || *endptr == 'G') ret *= 1024 * 1024 * 1024;
00075 return ret;
00076 }
00077
00078 void parse_args(int argc, char* argv[])
00079 {
00080
00081 while (1)
00082 {
00083 int opt;
00084 static struct option long_options[] = {
00085
00086 {"help", no_argument, 0, 'h'},
00087 {"seed", required_argument,0, 's'},
00088 {"hi", required_argument,0, 1},
00089 {"lo", required_argument,0, 2},
00090 {"min", required_argument,0, 3},
00091 {"max", required_argument,0, 4},
00092 {"no-seed", no_argument, 0, 5},
00093 {"ave", required_argument,0, 6},
00094 {"threads", required_argument,0, 7},
00095 {"time", required_argument,0, 't'},
00096 {0, 0, 0, 0}
00097 };
00098 opt = getopt_long(argc, argv, "?hvVs:t:",
00099 long_options, NULL);
00100 if (opt == -1) break;
00101 switch (opt)
00102 {
00103 case '?': case 'h': printf(USAGE);
00104 exit(0);
00105 break;
00106 case 'V': printf("%s\n", rev);
00107 exit(0);
00108 break;
00109 case 'v': ++opt_v;
00110 break;
00111 case 's': opt_seed = strtoul(optarg,NULL, 0);
00112 opt_seed_ = 1;
00113 break;
00114 case 't': opt_time = strtoul(optarg,NULL, 0);
00115 break;
00116 case 1: opt_hi_water = human(optarg);
00117 break;
00118 case 2: opt_lo_water = human(optarg);
00119 break;
00120 case 3: opt_min_alloc = human(optarg);
00121 break;
00122 case 4: opt_max_alloc = human(optarg);
00123 break;
00124 case 5: opt_seed_ = 0;
00125 break;
00126 case 6: opt_ave = strtoul(optarg,NULL, 0);
00127 break;
00128 case 7: opt_threads = strtoul(optarg,NULL, 0);
00129 break;
00130 default:
00131 printf("?? getopt returned character code 0%o ??\n", opt);
00132 exit(1);
00133 }
00134 }
00135 }
00136
00137 uint64_t gettimeofday_us(void)
00138 {
00139 struct timeval tv;
00140 gettimeofday(&tv, NULL);
00141 return (uint64_t)tv.tv_sec * 1000000 + tv.tv_usec;
00142 }
00143
00144 struct Stats
00145 {
00146 Stats() : count_(0)
00147 , running_tot_us_(0)
00148 , running_tot_MB_(0.0) {}
00149
00150 void update(double MB, int64_t delta_us)
00151 {
00152 mtx_.lock();
00153 running_tot_MB_ += MB;
00154 running_tot_us_ += delta_us;
00155 times_per_.insert((double)delta_us / MB);
00156 ++count_;
00157 mtx_.unlock();
00158 }
00159
00160 void get_clear(double& tot_MB, int64_t& tot_delta_us, int& count, double& min, double& max)
00161 {
00162 mtx_.lock();
00163 tot_MB = running_tot_MB_;
00164 tot_delta_us = running_tot_us_;
00165 count = count_;
00166 if (count == 0)
00167 min = max = 0;
00168 else
00169 {
00170 min = *(times_per_.begin());
00171 max = *(times_per_.rbegin());
00172 }
00173 running_tot_MB_ = 0.0;
00174 running_tot_us_ = 0;
00175 times_per_.clear();
00176 count_ = 0;
00177 mtx_.unlock();
00178 }
00179
00180 private:
00181 int count_;
00182 int64_t running_tot_us_;
00183 double running_tot_MB_;
00184 std::multiset<double> times_per_;
00185 std::mutex mtx_;
00186 };
00187
00188 struct Malloc
00189 {
00190 Malloc(Stats& stats) : num_mallocs(0)
00191 , stats_(stats) {}
00192
00193 #define NUM_DELTAS opt_ave
00194 int num_mallocs;
00195 Stats& stats_;
00196
00197
00198
00199 void* malloc_opt(unsigned* alloc_bytes)
00200 {
00201 void* ptr;
00202 if ((opt_max_alloc - opt_min_alloc) == 0)
00203 *alloc_bytes = opt_max_alloc;
00204 else
00205 *alloc_bytes = (random() % (opt_max_alloc - opt_min_alloc)) + opt_min_alloc;
00206
00207 uint64_t t0 = gettimeofday_us();
00208
00209 ptr = malloc(*alloc_bytes);
00210 if (ptr == NULL)
00211 {
00212 perror("malloc");
00213 exit(1);
00214 }
00215 unsigned ii;
00216 for (ii = 0; ii < *alloc_bytes; ii += 4096)
00217 *(int*)((char*)ptr + ii) = 1;
00218 ii /= 4096;
00219
00220 int64_t delta_us = gettimeofday_us() - t0;
00221 # define PAGES_PER_MB (0x100000/4096.0)
00222 double MB = (double)ii / PAGES_PER_MB;
00223 TRACE( 2,"malloc %u bytes ii=%u MB=%f\n", *alloc_bytes, ii, MB );
00224
00225 stats_.update(MB, delta_us);
00226
00227 return ptr;
00228 }
00229 };
00230
00231
00232 static void do_work(Stats* stats)
00233 {
00234 std::vector<std::pair<void*, int>> ptr_vec;
00235 uint64_t total_allocation = 0;
00236 int direction;
00237 enum
00238 {
00239 up,
00240 down
00241 };
00242
00243 Malloc mm(*stats);
00244
00245 while (total_allocation < opt_lo_water)
00246 {
00247 unsigned alloc_bytes;
00248 int8_t* ptr = (int8_t*)mm.malloc_opt(&alloc_bytes);
00249 std::pair<void*, int> xx(ptr, alloc_bytes);
00250 ptr_vec.push_back(xx);
00251 total_allocation += alloc_bytes;
00252 }
00253 direction = up;
00254
00255 printf("ptr_vec.size()=%ld\n", ptr_vec.size());
00256 while (!g_stop)
00257 {
00258 long rr = random();
00259
00260 long tst = (direction == up) ? RAND_MAX / 4 * 3 : RAND_MAX / 4 * 1;
00261 int malloc1_or_free0 = rr < tst;
00262
00263 if (malloc1_or_free0)
00264 {
00265
00266 unsigned alloc_bytes;
00267 int8_t* ptr = (int8_t*)mm.malloc_opt(&alloc_bytes);
00268 std::pair<void*, int> xx(ptr, alloc_bytes);
00269 ptr_vec.push_back(xx);
00270 total_allocation += alloc_bytes;
00271 if (total_allocation > opt_hi_water && direction == up)
00272 {
00273 TRACE( 1, "direction=down\n" );
00274 direction = down;
00275 }
00276 }
00277 else
00278 {
00279
00280 if (ptr_vec.size() == 0)
00281 {
00282 printf("size()==0 - continue\n");
00283 continue;
00284 }
00285 unsigned free_idx = random() % ptr_vec.size();
00286 std::pair<void*, int> xx(ptr_vec[free_idx]);
00287 ptr_vec.erase(ptr_vec.begin() + free_idx);
00288 TRACE( 2,"free %u\n",xx.second);
00289 free(xx.first);
00290 total_allocation -= xx.second;
00291 if (total_allocation < opt_lo_water && direction == down)
00292 {
00293 TRACE( 1, "direction=up\n" );
00294 direction = up;
00295 }
00296 }
00297 }
00298 }
00299
00300 void send_metrics(double ave, double min, double max, double MBperalloc, int32_t threads)
00301 {
00302 # define CLUSTER "mu2e DAQ"
00303 # define GROUP "memory"
00304 send_gmetric("ave", std::to_string(ave).c_str(), "double", "us/MB"
00305 , "both", 30, 0, GROUP, CLUSTER, "ave us/MB", "ave us/MB");
00306 send_gmetric("min", std::to_string(min).c_str(), "double", "us/MB"
00307 , "both", 30, 0, GROUP, CLUSTER, "min us/MB", "min us/MB");
00308 send_gmetric("max", std::to_string(max).c_str(), "double", "us/MB"
00309 , "both", 30, 0, GROUP, CLUSTER, "max us/MB", "max us/MB");
00310 send_gmetric("ave_MBperalloc", std::to_string(MBperalloc).c_str(), "double", "MB/alloc"
00311 , "both", 30, 0, GROUP, CLUSTER, "ave MB/alloc", "ave MB/alloc");
00312 send_gmetric("threads", std::to_string(threads).c_str(), "int32", "threads"
00313 , "both", 30, 0, GROUP, CLUSTER, "threads mallocing", "threads mallocing");
00314 }
00315
00316
00317 int main(int argc, char* argv[])
00318 {
00319 parse_args(argc, argv);
00320
00321 if (opt_hi_water < opt_lo_water)
00322 {
00323 printf("changeing hi_water from %lu to %lu\n", opt_hi_water, opt_lo_water);
00324 opt_hi_water = opt_lo_water;
00325 }
00326 if (opt_max_alloc < opt_min_alloc)
00327 {
00328 printf("changeing max_alloc from %u to %u\n", opt_max_alloc, opt_min_alloc);
00329 opt_max_alloc = opt_min_alloc;
00330 }
00331
00332 if (!opt_seed_)
00333 opt_seed = time(NULL) * getpid();
00334 srandom(opt_seed);
00335 printf("opt_seed=%u RAND_MAX=%d random=%ld\n", opt_seed, RAND_MAX, random());
00336 printf("lo=%ld hi=%ld min=%u max=%u\n", opt_lo_water, opt_hi_water, opt_min_alloc, opt_max_alloc);
00337
00338 init_gmetric("/etc/ganglia/gmond.conf");
00339
00340
00341 std::vector<boost::thread> threads(opt_threads);
00342 std::vector<Stats> stats(opt_threads);
00343 for (auto nn = 0; nn < opt_threads; ++nn)
00344 {
00345 boost::thread::attributes attrs;
00346 attrs.set_stack_size(4096 * 200);
00347 threads[nn] = boost::thread(attrs, boost::bind(&do_work, &stats[nn]));
00348 }
00349
00350 uint64_t start_us = gettimeofday_us();
00351 uint64_t end_us = start_us + (1000000L * opt_time);
00352 usleep(5000000);
00353 for (auto cc = 2; 1; ++cc)
00354 {
00355 double tot_ave_usperMB = 0.0, tot_min_usperMB = 0.0, tot_max_usperMB = 0.0, tot_MBperalloc = 0.0;
00356 for (auto nn = 0; nn < opt_threads; ++nn)
00357 {
00358 double tot_MB;
00359 int64_t tot_us;
00360 int count;
00361 double min, max;
00362 stats[nn].get_clear(tot_MB, tot_us, count, min, max);
00363 tot_ave_usperMB += (tot_MB != 0.0) ? (double)tot_us / tot_MB : 0.0;
00364 tot_min_usperMB += min;
00365 tot_max_usperMB += max;
00366 tot_MBperalloc += count ? tot_MB / count : 0;
00367 TRACE( 6, "nn=%d tot_MB=%.4f count=%d", nn, tot_MB, count );
00368 }
00369 if ((cc % 5) == 0)
00370 TRACE( 0,"%d ave malloc usec/MB ave=%.4f min=%.4f max=%.4f MB/alloc=%.1f\n"
00371 , opt_threads, tot_ave_usperMB/opt_threads, tot_min_usperMB/opt_threads
00372 , tot_max_usperMB/opt_threads, tot_MBperalloc/opt_threads );
00373
00374 send_metrics(tot_ave_usperMB / opt_threads
00375 , tot_min_usperMB / opt_threads
00376 , tot_max_usperMB / opt_threads
00377 , tot_MBperalloc / opt_threads
00378 , opt_threads);
00379
00380 uint64_t target = start_us + (5000000L * cc);
00381 uint64_t now_us = gettimeofday_us();
00382 int64_t slp = target - now_us;
00383 if (slp > 0)
00384 {
00385 if ((now_us + slp) > end_us)
00386 break;
00387 usleep(slp);
00388 }
00389 else
00390 {
00391 printf("cc=%d slp=%ld\n", cc, slp);
00392 if (now_us > end_us)
00393 break;
00394 }
00395 }
00396 send_metrics(0.0, 0.0, 0.0, 0.0, 0);
00397 g_stop = 1;
00398 for (auto nn = 0; nn < opt_threads; ++nn)
00399 {
00400 threads[nn].join();
00401 }
00402 printf("malloc_free finished.\n");
00403 return (0);
00404 }