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.
(This used to be commit ca7e4d9166a7cb10495cf3ecd9c4e72103fbec30)

source4/torture/config.mk
source4/torture/raw/lookuprate.c [new file with mode: 0644]
source4/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/source4/torture/raw/lookuprate.c b/source4/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);