Back to index

numactl  2.0.8~rc4
numademo.c
Go to the documentation of this file.
00001 /* Copyright (C) 2003,2004 Andi Kleen, SuSE Labs.
00002    Test/demo program for libnuma. This is also a more or less useful benchmark
00003    of the NUMA characteristics of your machine. It benchmarks most possible
00004    NUMA policy memory configurations with various benchmarks.
00005    Compile standalone with cc -O2 numademo.c -o numademo -lnuma -lm
00006 
00007    numactl is free software; you can redistribute it and/or
00008    modify it under the terms of the GNU General Public
00009    License as published by the Free Software Foundation; version
00010    2.
00011 
00012    numactl is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015    General Public License for more details.
00016 
00017    You should find a copy of v2 of the GNU General Public License somewhere
00018    on your Linux system; if not, write to the Free Software Foundation,
00019    Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
00020 #define _GNU_SOURCE 1
00021 #include <stdio.h>
00022 #include <string.h>
00023 #include <stdlib.h>
00024 #include <ctype.h>
00025 #include <sys/time.h>
00026 #include "numa.h"
00027 #ifdef HAVE_STREAM_LIB
00028 #include "stream_lib.h"
00029 #endif
00030 #ifdef HAVE_MT
00031 #include "mt.h"
00032 #endif
00033 #ifdef HAVE_CLEAR_CACHE
00034 #include "clearcache.h"
00035 #else
00036 static inline void clearcache(void *a, unsigned size) {}
00037 #endif
00038 #define FRACT_NODES 8
00039 #define FRACT_MASKS 32
00040 int fract_nodes;
00041 
00042 unsigned long msize;
00043 
00044 /* Should get this from cpuinfo, but on !x86 it's not there */
00045 enum {
00046        CACHELINESIZE = 64,
00047 };
00048 
00049 enum test {
00050        MEMSET = 0,
00051        MEMCPY,
00052        FORWARD,
00053        BACKWARD,
00054        STREAM,
00055        RANDOM2,
00056        PTRCHASE,
00057 } thistest;
00058 
00059 char *delim = " ";
00060 int force;
00061 int regression_testing=0;
00062 
00063 char *testname[] = {
00064        "memset",
00065        "memcpy",
00066        "forward",
00067        "backward",
00068 #ifdef HAVE_STREAM_LIB
00069        "stream",
00070 #endif
00071 #ifdef HAVE_MT
00072        "random2",
00073 #endif
00074        "ptrchase",
00075        NULL,
00076 };
00077 
00078 void output(char *title, char *result)
00079 {
00080        if (!isspace(delim[0]))
00081               printf("%s%s%s\n", title,delim, result);
00082        else
00083               printf("%-42s%s\n", title, result);
00084 }
00085 
00086 
00087 #ifdef HAVE_STREAM_LIB
00088 void do_stream(char *name, unsigned char *mem)
00089 {
00090        int i;
00091        char title[100], buf[100];
00092        double res[STREAM_NRESULTS];
00093        stream_verbose = 0;
00094        clearcache(mem, msize);
00095        stream_init(mem);
00096        stream_test(res);
00097        sprintf(title, "%s%s%s", name, delim, "STREAM");
00098        buf[0] = '\0';
00099        for (i = 0; i < STREAM_NRESULTS; i++) {
00100               if (buf[0])
00101                      strcat(buf,delim);
00102               sprintf(buf+strlen(buf), "%s%s%.2f%sMB/s",
00103                      stream_names[i], delim, res[i], delim);
00104        }
00105        output(title, buf);
00106        clearcache(mem, msize);
00107 }
00108 #endif
00109 
00110 /* Set up a randomly distributed list to fool prefetchers */
00111 union node {
00112        union node *next;
00113        struct {
00114               unsigned nexti;
00115               unsigned val;
00116        };
00117 };
00118 
00119 static int cmp_node(const void *ap, const void *bp)
00120 {
00121        union node *a = (union node *)ap;
00122        union node *b = (union node *)bp;
00123        return a->val - b->val;
00124 }
00125 
00126 void **ptrchase_init(unsigned char *mem)
00127 {
00128        long i;
00129        union node *nodes = (union node *)mem;
00130        long nmemb = msize / sizeof(union node);
00131        srand(1234);
00132        for (i = 0; i < nmemb; i++) {
00133               nodes[i].val = rand();
00134               nodes[i].nexti = i + 1;
00135        }
00136        qsort(nodes, nmemb, sizeof(union node), cmp_node);
00137        for (i = 0; i < nmemb; i++) {
00138               union node *n = &nodes[i];
00139               n->next = n->nexti >= nmemb ? NULL : &nodes[n->nexti];
00140        }
00141        return (void **)nodes;
00142 }
00143 
00144 static inline unsigned long long timerfold(struct timeval *tv)
00145 {
00146        return tv->tv_sec * 1000000ULL + tv->tv_usec;
00147 }
00148 
00149 #define LOOPS 10
00150 
00151 void memtest(char *name, unsigned char *mem)
00152 {
00153        long k;
00154        struct timeval start, end, res;
00155        unsigned long long max, min, sum, r;
00156        int i;
00157        char title[128], result[128];
00158 
00159        if (!mem) {
00160               fprintf(stderr,
00161               "Failed to allocate %lu bytes of memory. Test \"%s\" exits.\n",
00162                      msize, name);
00163               return;
00164        }
00165 
00166 #ifdef HAVE_STREAM_LIB
00167        if (thistest == STREAM) {
00168               do_stream(name, mem);
00169               goto out;
00170        }
00171 #endif
00172        
00173        max = 0;
00174        min = ~0UL;
00175        sum = 0;
00176 
00177        /*
00178         * Note:  0th pass allocates the pages, don't measure
00179         */
00180        for (i = 0; i < LOOPS+1; i++) {
00181               clearcache(mem, msize);
00182               switch (thistest) {
00183               case PTRCHASE:
00184               {
00185                      void **ptr;
00186                      ptr = ptrchase_init(mem);
00187                      gettimeofday(&start,NULL);
00188                      while (*ptr)
00189                             ptr = (void **)*ptr;
00190                      gettimeofday(&end,NULL);
00191                      /* Side effect to trick the optimizer */
00192                      *ptr = "bla";
00193                      break;
00194               }
00195 
00196               case MEMSET:
00197                      gettimeofday(&start,NULL);
00198                      memset(mem, 0xff, msize);
00199                      gettimeofday(&end,NULL);
00200                      break;
00201 
00202               case MEMCPY:
00203                      gettimeofday(&start,NULL);
00204                      memcpy(mem, mem + msize/2, msize/2);
00205                      gettimeofday(&end,NULL);
00206                      break;
00207 
00208               case FORWARD:
00209                      /* simple kernel to just fetch cachelines and write them back.
00210                         will trigger hardware prefetch */
00211                      gettimeofday(&start,NULL);
00212                      for (k = 0; k < msize; k+=CACHELINESIZE)
00213                             mem[k]++;
00214                      gettimeofday(&end,NULL);
00215                      break;
00216 
00217               case BACKWARD:
00218                      gettimeofday(&start,NULL);
00219                      for (k = msize-5; k > 0; k-=CACHELINESIZE)
00220                             mem[k]--;
00221                      gettimeofday(&end,NULL);
00222                      break;
00223 
00224 #ifdef HAVE_MT
00225               case RANDOM2:
00226               {
00227                      unsigned * __restrict m = (unsigned *)mem;
00228                      unsigned max = msize / sizeof(unsigned);
00229                      unsigned mask;
00230 
00231                      mt_init();
00232                      mask = 1;
00233                      while (mask < max)
00234                             mask = (mask << 1) | 1;
00235                      /*
00236                       * There's no guarantee all memory is touched, but
00237                       * we assume (hope) that the distribution of the MT
00238                       * is good enough to touch most.
00239                       */
00240                      gettimeofday(&start,NULL);
00241                      for (k = 0; k < max; k++) {
00242                             unsigned idx = mt_random() & mask;
00243                             if (idx >= max)
00244                                    idx -= max;
00245                             m[idx]++;
00246                      }
00247                      gettimeofday(&end,NULL);
00248               }
00249 
00250 #endif
00251               default:
00252                      break;
00253               }
00254 
00255               if (!i)
00256                      continue;  /* don't count allocation pass */
00257 
00258               timersub(&end, &start, &res);
00259               r = timerfold(&res);
00260               if (r > max) max = r;
00261               if (r < min) min = r;
00262               sum += r;
00263        }
00264        sprintf(title, "%s%s%s", name, delim, testname[thistest]);
00265 #define H(t) (((double)msize) / ((double)t))
00266 #define D3 delim,delim,delim
00267        sprintf(result, "Avg%s%.2f%sMB/s%sMax%s%.2f%sMB/s%sMin%s%.2f%sMB/s",
00268               delim,
00269               H(sum/LOOPS),
00270               D3,
00271               H(min),
00272               D3,
00273               H(max),
00274               delim);
00275 #undef H
00276 #undef D3
00277        output(title,result);
00278 
00279 #ifdef HAVE_STREAM_LIB
00280  out:
00281 #endif
00282        /* Just to make sure that when we switch CPUs that the old guy
00283           doesn't still keep it around. */
00284        clearcache(mem, msize);
00285 
00286        numa_free(mem, msize);
00287 }
00288 
00289 int popcnt(unsigned long val)
00290 {
00291        int i = 0, cnt = 0;
00292        while (val >> i) {
00293               if ((1UL << i) & val)
00294                      cnt++;
00295               i++;
00296        }
00297        return cnt;
00298 }
00299 
00300 int max_node;
00301 
00302 void test(enum test type)
00303 {
00304        unsigned long mask;
00305        int i, k;
00306        char buf[512];
00307        struct bitmask *nodes;
00308 
00309        nodes = numa_allocate_nodemask();
00310        thistest = type;
00311 
00312        if (regression_testing) {
00313               printf("\nTest %s doing 1 of %d nodes and 1 of %d masks.\n",
00314                      testname[thistest], fract_nodes, FRACT_MASKS);
00315        }
00316 
00317        memtest("memory with no policy", numa_alloc(msize));
00318        memtest("local memory", numa_alloc_local(msize));
00319 
00320        memtest("memory interleaved on all nodes", numa_alloc_interleaved(msize));
00321        for (i = 0; i <= max_node; i++) {
00322               if (regression_testing && (i % fract_nodes)) {
00323               /* for regression testing (-t) do only every eighth node */
00324                      continue;
00325               }
00326               sprintf(buf, "memory on node %d", i);
00327               memtest(buf, numa_alloc_onnode(msize, i));
00328        }
00329        
00330        for (mask = 1, i = 0; mask < (1UL<<(max_node+1)); mask++, i++) {
00331               int w;
00332               char buf2[10];
00333               if (popcnt(mask) == 1)
00334                      continue;
00335               if (regression_testing && (i > 50)) {
00336                      break;
00337               }
00338               if (regression_testing && (i % FRACT_MASKS)) {
00339               /* for regression testing (-t)
00340                      do only every 32nd mask permutation */
00341                      continue;
00342               }
00343               numa_bitmask_clearall(nodes);
00344               for (w = 0; mask >> w; w++) {
00345                      if ((mask >> w) & 1)
00346                             numa_bitmask_setbit(nodes, w);
00347               }
00348 
00349               sprintf(buf, "memory interleaved on");
00350               for (k = 0; k <= max_node; k++)
00351                      if ((1UL<<k) & mask) {
00352                             sprintf(buf2, " %d", k);
00353                             strcat(buf, buf2);
00354                      }
00355               memtest(buf, numa_alloc_interleaved_subset(msize, nodes));
00356        }
00357 
00358        for (i = 0; i <= max_node; i++) {
00359               if (regression_testing && (i % fract_nodes)) {
00360               /* for regression testing (-t) do only every eighth node */
00361                      continue;
00362               }
00363               printf("setting preferred node to %d\n", i);
00364               numa_set_preferred(i);
00365               memtest("memory without policy", numa_alloc(msize));
00366        }
00367 
00368        numa_set_interleave_mask(numa_all_nodes_ptr);
00369        memtest("manual interleaving to all nodes", numa_alloc(msize));
00370 
00371        if (max_node > 0) {
00372               numa_bitmask_clearall(nodes);
00373               numa_bitmask_setbit(nodes, 0);
00374               numa_bitmask_setbit(nodes, 1);
00375               numa_set_interleave_mask(nodes);
00376               memtest("manual interleaving on node 0/1", numa_alloc(msize));
00377               printf("current interleave node %d\n", numa_get_interleave_node());
00378        }
00379 
00380        numa_set_interleave_mask(numa_no_nodes_ptr);
00381 
00382        nodes = numa_allocate_nodemask();
00383 
00384        for (i = 0; i <= max_node; i++) {
00385               int oldhn = numa_preferred();
00386 
00387               if (regression_testing && (i % fract_nodes)) {
00388               /* for regression testing (-t) do only every eighth node */
00389                      continue;
00390               }
00391               numa_run_on_node(i);
00392               printf("running on node %d, preferred node %d\n",i, oldhn);
00393 
00394               memtest("local memory", numa_alloc_local(msize));
00395 
00396               memtest("memory interleaved on all nodes",
00397                      numa_alloc_interleaved(msize));
00398 
00399               if (max_node >= 1) {
00400                      numa_bitmask_clearall(nodes);
00401                      numa_bitmask_setbit(nodes, 0);
00402                      numa_bitmask_setbit(nodes, 1);
00403                      memtest("memory interleaved on node 0/1",
00404                             numa_alloc_interleaved_subset(msize, nodes));
00405               }
00406 
00407               for (k = 0; k <= max_node; k++) {
00408                      if (k == i)
00409                             continue;
00410                      if (regression_testing && (k % fract_nodes)) {
00411                      /* for regression testing (-t)
00412                             do only every eighth node */
00413                             continue;
00414                      }
00415                      sprintf(buf, "alloc on node %d", k);
00416                      numa_bitmask_clearall(nodes);
00417                      numa_bitmask_setbit(nodes, k);
00418                      numa_set_membind(nodes);
00419                      memtest(buf, numa_alloc(msize));                 
00420                      numa_set_membind(numa_all_nodes_ptr);
00421               }
00422               
00423               numa_set_localalloc();
00424               memtest("local allocation", numa_alloc(msize));
00425 
00426               numa_set_preferred((i+1) % (1+max_node));
00427               memtest("setting wrong preferred node", numa_alloc(msize));
00428               numa_set_preferred(i);
00429               memtest("setting correct preferred node", numa_alloc(msize));
00430               numa_set_preferred(-1);
00431               if (!delim[0])
00432                      printf("\n\n\n");
00433        }
00434 
00435        /* numa_run_on_node_mask is not tested */
00436 }
00437 
00438 void usage(void)
00439 {
00440        int i;
00441        printf("usage: numademo [-S] [-f] [-c] [-e] [-t] msize[kmg] {tests}\nNo tests means run all.\n");
00442        printf("-c output CSV data. -f run even without NUMA API. -S run stupid tests. -e exit on error\n");
00443        printf("-t regression test; do not run all node combinations\n");
00444        printf("valid tests:");
00445        for (i = 0; testname[i]; i++)
00446               printf(" %s", testname[i]);
00447        putchar('\n');
00448        exit(1);
00449 }
00450 
00451 /* duplicated to make numademo standalone */
00452 long memsize(char *s)
00453 {
00454        char *end;
00455        long length = strtoul(s,&end,0);
00456        switch (toupper(*end)) {
00457        case 'G': length *= 1024;  /*FALL THROUGH*/
00458        case 'M': length *= 1024;  /*FALL THROUGH*/
00459        case 'K': length *= 1024; break;
00460        }
00461        return length;
00462 }
00463 
00464 int main(int ac, char **av)
00465 {
00466        int simple_tests = 0;
00467        
00468        while (av[1] && av[1][0] == '-') {
00469               ac--;
00470               switch (av[1][1]) {
00471               case 'c':
00472                      delim = ",";
00473                      break;
00474               case 'f':
00475                      force = 1;
00476                      break;
00477               case 'S':
00478                      simple_tests = 1;
00479                      break;
00480               case 'e':
00481                      numa_exit_on_error = 1;
00482                      numa_exit_on_warn = 1;
00483                      break;
00484               case 't':
00485                      regression_testing = 1;
00486                      break;
00487               default:
00488                      usage();
00489                      break;
00490               }
00491               ++av;
00492        }
00493 
00494        if (!av[1])
00495               usage();
00496 
00497        if (numa_available() < 0) {
00498               printf("your system does not support the numa API.\n");
00499               if (!force)
00500                      exit(1);
00501        }
00502 
00503        max_node = numa_max_node();
00504        printf("%d nodes available\n", max_node+1);
00505        fract_nodes = ((max_node/8)*2) + FRACT_NODES;
00506 
00507        if (max_node <= 2)
00508               regression_testing = 0; /* set -t auto-off for small systems */
00509 
00510        msize = memsize(av[1]);
00511 
00512        if (!msize)
00513               usage();
00514 
00515 #ifdef HAVE_STREAM_LIB
00516        stream_setmem(msize);
00517 #endif
00518 
00519        if (av[2] == NULL) {
00520               test(MEMSET);
00521               test(MEMCPY);
00522               if (simple_tests) {
00523                      test(FORWARD);
00524                      test(BACKWARD);
00525               }
00526 #ifdef HAVE_MT
00527               test(RANDOM2);
00528 #endif
00529 #ifdef HAVE_STREAM_LIB
00530               test(STREAM);
00531 #endif
00532               if (msize >= sizeof(union node)) {
00533                      test(PTRCHASE);
00534               } else {
00535                      fprintf(stderr, "You must set msize at least %lu bytes for ptrchase test.\n",
00536                             sizeof(union node));
00537                      exit(1);
00538               }
00539        } else {
00540               int k;
00541               for (k = 2; k < ac; k++) {
00542                      int i;
00543                      int found = 0;
00544                      for (i = 0; testname[i]; i++) {
00545                             if (!strcmp(testname[i],av[k])) {
00546                                    test(i);
00547                                    found = 1;
00548                                    break;
00549                             }
00550                      }
00551                      if (!found) {
00552                             fprintf(stderr,"unknown test `%s'\n", av[k]);
00553                             usage();
00554                      }
00555               }
00556        }
00557        return 0;
00558 }