memcache_test.c
changeset 49 10c7dce1a043
parent 48 1c67f512779b
--- a/memcache_test.c	Fri Aug 29 23:31:17 2008 +0300
+++ b/memcache_test.c	Sat Aug 30 19:13:15 2008 +0300
@@ -39,6 +39,7 @@
 
 struct common {
     unsigned int max_connections;
+    char pipeline_requests;
     
     struct timeval start;
 } common;
@@ -65,10 +66,18 @@
 void benchmark_cb (struct memcache_req *req, void *arg);
 void benchmark_fetch_fn (void);
 
+enum option_test {
+    TEST_INVALID,
+    TEST_COMMON,
+
+    TEST_BENCH_FETCH,
+};
+
 enum option_code {
     OPT_CODE_INVALID,
 
     COMMON_CONN_MAX,
+    COMMON_PIPELINE,
     BENCH_FETCH_REQ_CONCURRENCY,
     BENCH_FETCH_REQ_AMOUNT,
     BENCH_FETCH_KEY_PREFIX,
@@ -83,6 +92,7 @@
 
 enum option_type {
     OPT_TYPE_NONE,
+    OPT_TYPE_BOOL,
     OPT_TYPE_UINT,
     OPT_TYPE_STR,
 };
@@ -93,13 +103,19 @@
 
     void (*test_fn) (void);
 
+    enum option_test code;
+
+    char *descr;
+
 } test_list[] = {
-    { "benchmark_fetch",        &benchmark_cb,  &benchmark_fetch_fn },
-    { 0,                        0,              0,                  }
+    { "benchmark_fetch",        &benchmark_cb,  &benchmark_fetch_fn,    TEST_BENCH_FETCH,
+        "Measure the speed of fetch requests"                                               },
+    { 0,                        0,              0,                      0,  0               }
 };
 
 static struct option options[] = {
     { "conn-max",           required_argument,  NULL,   COMMON_CONN_MAX             },
+    { "pipeline",           required_argument,  NULL,   COMMON_PIPELINE             },
     { "req-concurrency",    required_argument,  NULL,   BENCH_FETCH_REQ_CONCURRENCY },
     { "req-amount",         required_argument,  NULL,   BENCH_FETCH_REQ_AMOUNT      },
     { "key-prefix",         required_argument,  NULL,   BENCH_FETCH_KEY_PREFIX      },
@@ -112,10 +128,16 @@
 };
 
 static struct opt {
+    enum option_test test;
     enum option_code code;
     enum option_type type;
     
     union opt_type_data {
+        struct  {
+            char *value;
+            char default_value;
+        } bool;
+
         struct {
             unsigned int *value;
             unsigned int default_value;
@@ -126,17 +148,41 @@
             const char *default_value;
         } str;
     } data;
+    
+    const char *name;
+    const char *descr;
 } option_info[OPT_CODE_MAX] = {
-    {   OPT_CODE_INVALID,               OPT_TYPE_NONE                                                           },
-    {   COMMON_CONN_MAX,                OPT_TYPE_UINT,  { .uint = { &common.max_connections,          1     }}  },
-    {   BENCH_FETCH_REQ_CONCURRENCY,    OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.concurrent_ops,  1     }}  },
-    {   BENCH_FETCH_REQ_AMOUNT,         OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.total_ops,       500   }}  },
-    {   BENCH_FETCH_KEY_PREFIX,         OPT_TYPE_STR,   { .str  = { &benchmark_fetch.key_prefix,      "bf_" }}  },
-    {   BENCH_FETCH_KEY_LEN_MIN,        OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.key_len_min,     8     }}  },
-    {   BENCH_FETCH_KEY_LEN_MAX,        OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.key_len_max,     8     }}  },
-    {   BENCH_FETCH_KEY_COUNT,          OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.key_count,       1     }}  },
-    {   BENCH_FETCH_DATA_LEN_MIN,       OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.data_len_min,    64    }}  },
-    {   BENCH_FETCH_DATA_LEN_MAX,       OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.data_len_max,    64    }}  },
+    {   TEST_INVALID,       OPT_CODE_INVALID,               OPT_TYPE_NONE                                                           },
+
+    {   TEST_COMMON,        COMMON_CONN_MAX,                OPT_TYPE_UINT,  { .uint = { &common.max_connections,          1     }},
+            "conn-max",             "number of connections to use"                                                                  },
+
+    {   TEST_COMMON,        COMMON_PIPELINE,                OPT_TYPE_BOOL,  { .bool = { &common.pipeline_requests,        1     }},  
+            "pipeline",             "pipeline requests"                                                                             },
+
+    {   TEST_BENCH_FETCH,   BENCH_FETCH_REQ_CONCURRENCY,    OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.concurrent_ops,  1     }},
+            "req-concurrency",      "number of requests to have running"                                                            },
+
+    {   TEST_BENCH_FETCH,   BENCH_FETCH_REQ_AMOUNT,         OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.total_ops,       500   }},
+            "req-amount",           "number of requests to issue"                                                                   },
+
+    {   TEST_BENCH_FETCH,   BENCH_FETCH_KEY_PREFIX,         OPT_TYPE_STR,   { .str  = { &benchmark_fetch.key_prefix,      "bf_" }},
+            "key-prefix",           "key prefix"                                                                                    },
+
+    {   TEST_BENCH_FETCH,   BENCH_FETCH_KEY_LEN_MIN,        OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.key_len_min,     8     }},  
+            "key-len-min",          "minimum key length"                                                                            },
+
+    {   TEST_BENCH_FETCH,   BENCH_FETCH_KEY_LEN_MAX,        OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.key_len_max,     8     }},
+            "key-len-max",          "maximum key length"                                                                            },
+
+    {   TEST_BENCH_FETCH,   BENCH_FETCH_KEY_COUNT,          OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.key_count,       1     }},
+            "key-count",            "how many keys to use"                                                                          },
+
+    {   TEST_BENCH_FETCH,   BENCH_FETCH_DATA_LEN_MIN,       OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.data_len_min,    64    }},
+            "data-len-min",         "minimum data length"                                                                           },
+
+    {   TEST_BENCH_FETCH,   BENCH_FETCH_DATA_LEN_MAX,       OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.data_len_max,    64    }},
+            "data-len-max",         "maximum data length"                                                                            },
 };
 
 void time_reset () {
@@ -152,11 +198,15 @@
     return ((double) (time.tv_sec - common.start.tv_sec)) + ((double) (time.tv_usec - common.start.tv_usec)) / 1000000;
 }
 
+int ratelimit (int count, int total) {
+    return ((total / 10) ? (count % (total / 10) == 0) : 1);
+}
+
 
 
 void mc_init (int max_connections, memcache_cb cb_fn) {
     // memcache init    
-    if ((mc = memcache_alloc(cb_fn)) == NULL)
+    if ((mc = memcache_alloc(cb_fn, common.pipeline_requests)) == NULL)
         ERROR("memcache_alloc");
     
     // fix up the endpoint
@@ -166,10 +216,10 @@
         ERROR("config_endpoint_parse");
     
     // add the server
-    if (memcache_add_server(mc, &server_endpoint, max_connections))
+    if (memcache_add_server(mc, &server_endpoint, common.max_connections))
         ERROR("memcache_add_server");
     
-    INFO("[memcache] initialized with max_connections=%d", max_connections);
+    INFO("[memcache] initialized with pipeline_requests=%c max_connections=%d", common.pipeline_requests ? 't' : 'f', common.max_connections);
 
     return;
 
@@ -181,7 +231,7 @@
     const struct memcache_obj *obj;
     const struct memcache_buf *buf;
 
-    INFO("[%*s]: cmd=%s state=%s reply=%s",
+    INFO("[%.*s]: cmd=%s state=%s reply=%s",
         (int) memcache_req_key(req)->len, memcache_req_key(req)->buf,
         memcache_command_str(memcache_req_cmd(req)),
         memcache_state_str(memcache_req_state(req)),
@@ -288,8 +338,8 @@
         assert(memcache_fetch(mc, &benchmark_fetch.keys[random_value(0, benchmark_fetch.key_count)].key, NULL) != NULL);
 
         benchmark_fetch.cur_ops++;
-
-        if ((benchmark_fetch.op_count + benchmark_fetch.cur_ops) % (benchmark_fetch.total_ops / 10) == 0)
+        
+        if (ratelimit(benchmark_fetch.op_count + benchmark_fetch.cur_ops, benchmark_fetch.total_ops))
             INFO("[benchmark] %0.6f: %d+%d/%d requests", 
                 time_offset(),
                 benchmark_fetch.op_count, benchmark_fetch.cur_ops, benchmark_fetch.total_ops
@@ -357,8 +407,8 @@
             }
 
             benchmark_fetch.keys_stored++;
-
-            if (benchmark_fetch.keys_stored % (benchmark_fetch.key_count / 10) == 0)
+            
+            if (ratelimit(benchmark_fetch.keys_stored, benchmark_fetch.key_count))
                 INFO("[benchmark] %.6f: key %u/%u stored: %.*s", 
                     time_offset(), benchmark_fetch.keys_stored, benchmark_fetch.key_count, (int) memcache_req_key(req)->len, memcache_req_key(req)->buf
                 );
@@ -419,27 +469,51 @@
     }
 }
 
+void usage_opt (enum option_test test) {
+    struct opt *opt;
+
+    for (opt = option_info + 1; opt->code; opt++) {
+        if (opt->test == test) {
+            switch (opt->type) {
+                case OPT_TYPE_BOOL:
+                    INFO("\t%-20s %c     %s", opt->name, opt->data.bool.default_value ? 't' : 'f', opt->descr);
+                    break;
+
+                case OPT_TYPE_UINT:
+                    INFO("\t%-20s %-6d %s", opt->name, opt->data.uint.default_value, opt->descr);
+                    break;
+
+                case OPT_TYPE_STR:
+                    INFO("\t%-20s %-6s %s", opt->name, opt->data.str.default_value, opt->descr);
+                    break;
+                
+                default:
+                    assert(0);
+            }
+        }
+    }
+}
+
 void usage (char *cmd) {
-    INFO(
-        "Usage: %s <cmd> [<args> ... ]\n"
-        "\n"
-        "COMMANDS\n"
-        "\n"
-        "benchmark_fetch:\n"
-        "\tMeasure the speed of fetch requests\n"
-        "\t\n"
-        "\tconn-max         1       number of connections to use\n"
-        "\treq-concurrency  1       number of requests to have running\n"
-        "\treq-amount       500     number of requests to issue\n"
-        "\tkey-prefix       bf_     key prefix\n"
-        "\tkey-len-min      8       minimum key lengt\n"
-        "\tkey-len-max      8       maximum key length\n"
-        "\tkey-count        1       how many keys to use\n"
-        "\tdata-len-min     64      minimum data length\n"
-        "\tdata-len-max     64      maximum data length\n",
+    struct test *test;
 
-        cmd
-    );
+    INFO("Usage: %s <cmd> [<args> ... ]", cmd);
+
+    INFO("\nCOMMON OPTIONS\n");
+    
+    usage_opt(TEST_COMMON);
+
+    INFO("\nCOMMANDS\n");
+
+    for (test = test_list; test->name; test++) {
+        INFO("%s:", test->name);
+        INFO("\t%s", test->descr);
+        INFO("\t");
+        
+        usage_opt(test->code);
+
+        INFO("\t");
+    }
 
     exit(1);
 }
@@ -472,6 +546,11 @@
         switch (option_info[c].type) {
             case OPT_TYPE_NONE:
                 break;
+            
+            case OPT_TYPE_BOOL:
+                *option_info[c].data.bool.value = option_info[c].data.bool.default_value;
+
+                break;
 
             case OPT_TYPE_UINT:
                 *option_info[c].data.uint.value = option_info[c].data.uint.default_value;
@@ -491,8 +570,31 @@
     while ((c = getopt_long(argc, argv, "", options, &option_index)) != -1) {
         if (c <= OPT_CODE_INVALID || c >= OPT_CODE_MAX)
             FATAL("invalid argument %s", options[option_index].name);
+
+        if (option_info[c].test != test->code && option_info[c].test != TEST_COMMON)
+            FATAL("invalid option %s for test %s", options[option_index].name, test->name);
         
         switch (option_info[c].type) {
+            case OPT_TYPE_BOOL:
+                assert(optarg);
+
+                switch (optarg[0]) {
+                    case 't':
+                    case '1':
+                        *option_info[c].data.bool.value = 1;
+                        break;
+
+                    case 'f':
+                    case '0':
+                        *option_info[c].data.bool.value = 0;
+                        break;
+
+                    default:
+                        FATAL("invalid true/false value: %s: %s", options[option_index].name, optarg);
+                }
+
+                break;
+
             case OPT_TYPE_UINT:
                 assert(optarg);