smbtorture: Add RAW-BENCH-LOOKUP micro-benchmark.
authorJames Peach <jpeach@apple.com>
Mon, 21 Apr 2008 22:09:43 +0000 (15:09 -0700)
committerJames Peach <jpeach@apple.com>
Mon, 21 Apr 2008 22:09:43 +0000 (15:09 -0700)
This test samples the lookup rate for a non-existant filename in a
directory, while varying the number of files in the directory. The
lookup rate should continue to approximate the lookup rate for the
empty directory case.

source/torture/config.mk
source/torture/raw/lookuprate.c [new file with mode: 0644]
source/torture/raw/raw.c

index 2f3fa38863f8f371dce974d43b128497ea5a2057..1565797609d13e26cdb5d6cc158a57a3c1376fb3 100644 (file)
@@ -86,6 +86,7 @@ TORTURE_RAW_OBJ_FILES = $(addprefix torture/raw/, \
                lock.o \
                pingpong.o \
                lockbench.o \
+               lookuprate.o \
                openbench.o \
                rename.o \
                eas.o \
diff --git a/source/torture/raw/lookuprate.c b/source/torture/raw/lookuprate.c
new file mode 100644 (file)
index 0000000..4b505dc
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+   File lookup rate test.
+
+   Copyright (C) James Peach 2006
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "param/param.h"
+#include "system/filesys.h"
+#include "torture/torture.h"
+#include "torture/basic/proto.h"
+#include "libcli/libcli.h"
+#include "lib/cmdline/popt_common.h"
+#include "auth/credentials/credentials.h"
+
+#define BASEDIR "\\lookuprate"
+#define MISSINGNAME BASEDIR "\\foo"
+
+#define FUZZ_PERCENT 10
+
+#define usec_to_sec(s) ((s) / 1000000)
+#define sec_to_usec(s) ((s) * 1000000)
+
+struct rate_record
+{
+    unsigned   dirent_count;
+    unsigned   querypath_persec;
+    unsigned   findfirst_persec;
+};
+
+static struct rate_record records[] =
+{
+    { 0, 0, 0 },       /* Base (optimal) lookup rate. */
+    { 100, 0, 0},
+    { 1000, 0, 0},
+    { 10000, 0, 0},
+    { 100000, 0, 0}
+};
+
+typedef NTSTATUS lookup_function(struct smbcli_tree *tree, const char * path);
+
+/* Test whether rhs is within fuzz% of lhs. */
+static bool fuzzily_equal(unsigned lhs, unsigned rhs, int percent)
+{
+       double fuzz = (double)lhs * (double)percent/100.0;
+
+       if (((double)rhs >= ((double)lhs - fuzz)) &&
+           ((double)rhs <= ((double)lhs + fuzz))) {
+               return true;
+       }
+
+       return false;
+
+}
+
+static NTSTATUS fill_directory(struct smbcli_tree *tree,
+           const char * path, unsigned count)
+{
+       NTSTATUS        status;
+       char            *fname = NULL;
+       unsigned        i;
+       unsigned        current;
+
+       struct timeval start;
+       struct timeval now;
+
+       status = smbcli_mkdir(tree, path);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       printf("filling directory %s with %u files... ", path, count);
+       fflush(stdout);
+
+       current = random();
+       start = timeval_current();
+
+       for (i = 0; i < count; ++i) {
+               int fnum;
+
+               ++current;
+               fname = talloc_asprintf(NULL, "%s\\fill%u",
+                                   path, current);
+
+               fnum = smbcli_open(tree, fname, O_RDONLY|O_CREAT,
+                               OPENX_MODE_DENY_NONE);
+               if (fnum < 0) {
+                       talloc_free(fname);
+                       return smbcli_nt_error(tree);
+               }
+
+               smbcli_close(tree, fnum);
+               talloc_free(fname);
+       }
+
+       if (count) {
+               double rate;
+               now = timeval_current();
+               rate = (double)count / usec_to_sec((double)usec_time_diff(&now, &start));
+               printf("%u/sec\n", (unsigned)rate);
+       } else {
+               printf("done\n");
+       }
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS squash_lookup_error(NTSTATUS status)
+{
+       if (NT_STATUS_IS_OK(status)) {
+               return NT_STATUS_OK;
+       }
+
+       /* We don't care if the file isn't there. */
+       if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
+               return NT_STATUS_OK;
+       }
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+               return NT_STATUS_OK;
+       }
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_FILE)) {
+               return NT_STATUS_OK;
+       }
+
+       return status;
+}
+
+/* Look up a pathname using TRANS2_QUERY_PATH_INFORMATION. */
+static NTSTATUS querypath_lookup(struct smbcli_tree *tree, const char * path)
+{
+       NTSTATUS        status;
+       time_t          ftimes[3];
+       size_t          fsize;
+       uint16_t        fmode;
+
+       status = smbcli_qpathinfo(tree, path, &ftimes[0], &ftimes[1], &ftimes[2],
+                       &fsize, &fmode);
+
+       return squash_lookup_error(status);
+}
+
+/* Look up a pathname using TRANS2_FIND_FIRST2. */
+static NTSTATUS findfirst_lookup(struct smbcli_tree *tree, const char * path)
+{
+       NTSTATUS status = NT_STATUS_OK;
+
+       if (smbcli_list(tree, path, 0, NULL, NULL) < 0) {
+               status = smbcli_nt_error(tree);
+       }
+
+       return squash_lookup_error(status);
+}
+
+static NTSTATUS lookup_rate_convert(struct smbcli_tree *tree,
+       lookup_function lookup, const char * path, unsigned * rate)
+{
+       NTSTATUS        status;
+
+       struct timeval  start;
+       struct timeval  now;
+       unsigned        count = 0;
+       int64_t         elapsed = 0;
+
+#define LOOKUP_PERIOD_SEC (2)
+
+       start = timeval_current();
+       while (elapsed < sec_to_usec(LOOKUP_PERIOD_SEC)) {
+
+               status = lookup(tree, path);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               ++count;
+               now = timeval_current();
+               elapsed = usec_time_diff(&now, &start);
+       }
+
+#undef LOOKUP_PERIOD_SEC
+
+       *rate = (unsigned)((double)count / (double)usec_to_sec(elapsed));
+       return NT_STATUS_OK;
+}
+
+static bool remove_working_directory(struct smbcli_tree *tree,
+               const char * path)
+{
+       int tries;
+
+       /* Using smbcli_deltree to delete a very large number of files
+        * doesn't work against all servers. Work around this by
+        * retrying.
+        */
+       for (tries = 0; tries < 5; ) {
+               int ret;
+
+               ret = smbcli_deltree(tree, BASEDIR);
+               if (ret == -1) {
+                       tries++;
+                       printf("(%s) failed to deltree %s: %s\n",
+                               __location__, BASEDIR,
+                               smbcli_errstr(tree));
+                       continue;
+               }
+
+               return true;
+       }
+
+       return false;
+
+}
+
+/* Verify that looking up a file name takes constant time.
+ *
+ * This test samples the lookup rate for a non-existant filename in a
+ * directory, while varying the number of files in the directory. The
+ * lookup rate should continue to approximate the lookup rate for the
+ * empty directory case.
+ */
+bool torture_bench_lookup(struct torture_context *torture)
+{
+       NTSTATUS        status;
+       bool            result = false;
+
+       int i, tries;
+       struct smbcli_state *cli = NULL;
+
+       if (!torture_open_connection(&cli, torture, 0)) {
+               goto done;
+       }
+
+       remove_working_directory(cli->tree, BASEDIR);
+
+       for (i = 0; i < ARRAY_SIZE(records); ++i) {
+               printf("testing lookup rate with %u directory entries\n",
+                               records[i].dirent_count);
+
+               status = fill_directory(cli->tree, BASEDIR,
+                               records[i].dirent_count);
+               if (!NT_STATUS_IS_OK(status)) {
+                       printf("failed to fill directory: %s\n", nt_errstr(status));
+                       goto done;
+               }
+
+               status = lookup_rate_convert(cli->tree, querypath_lookup,
+                       MISSINGNAME, &records[i].querypath_persec);
+               if (!NT_STATUS_IS_OK(status)) {
+                       printf("querypathinfo of %s failed: %s\n",
+                               MISSINGNAME, nt_errstr(status));
+                       goto done;
+               }
+
+               status = lookup_rate_convert(cli->tree, findfirst_lookup,
+                       MISSINGNAME, &records[i].findfirst_persec);
+               if (!NT_STATUS_IS_OK(status)) {
+                       printf("findfirst of %s failed: %s\n",
+                               MISSINGNAME, nt_errstr(status));
+                       goto done;
+               }
+
+               printf("entries = %u, querypath = %u/sec, findfirst = %u/sec\n",
+                               records[i].dirent_count,
+                               records[i].querypath_persec,
+                               records[i].findfirst_persec);
+
+               if (!remove_working_directory(cli->tree, BASEDIR)) {
+                       goto done;
+               }
+       }
+
+       /* Ok. We have run all our tests. Walk through the records we
+        * accumulated and figure out whether the lookups took constant
+        * time of not.
+        */
+       for (i = 0; i < ARRAY_SIZE(records); ++i) {
+               if (!fuzzily_equal(records[0].querypath_persec,
+                                   records[i].querypath_persec,
+                                   FUZZ_PERCENT)) {
+                       printf("querypath rate for %d entries differed by "
+                               "more than %d%% from base rate\n",
+                               records[i].dirent_count, FUZZ_PERCENT);
+                       result = false;
+               }
+
+               if (!fuzzily_equal(records[0].findfirst_persec,
+                                   records[i].findfirst_persec,
+                                   FUZZ_PERCENT)) {
+                       printf("findfirst rate for %d entries differed by "
+                               "more than %d%% from base rate\n",
+                               records[i].dirent_count, FUZZ_PERCENT);
+                       result = false;
+               }
+       }
+
+done:
+       if (cli) {
+               remove_working_directory(cli->tree, BASEDIR);
+               talloc_free(cli);
+       }
+
+       return result;
+}
+
+/* vim: set sts=8 sw=8 : */
index bb3dde728fd44338d67e212b30d26b6abeb83cf2..262ed1384bf6a0ab956367346660b55f90bd0034 100644 (file)
@@ -33,6 +33,8 @@ NTSTATUS torture_raw_init(void)
        torture_suite_add_simple_test(suite, "PING-PONG", torture_ping_pong);
        torture_suite_add_simple_test(suite, "BENCH-LOCK", torture_bench_lock);
        torture_suite_add_simple_test(suite, "BENCH-OPEN", torture_bench_open);
+       torture_suite_add_simple_test(suite, "BENCH-LOOKUP",
+               torture_bench_lookup);
        torture_suite_add_simple_test(suite, "OFFLINE", torture_test_offline);
        torture_suite_add_1smb_test(suite, "QFSINFO", torture_raw_qfsinfo);
        torture_suite_add_1smb_test(suite, "QFILEINFO", torture_raw_qfileinfo);