7accf3f2383725da5c8ffb845c0caf684a0a4cf9
markd
  Sat Apr 3 12:50:44 2021 -0700
added oneshot program for testing mmap performance

diff --git src/oneShot/mmapPerf/mmapPerf.c src/oneShot/mmapPerf/mmapPerf.c
new file mode 100644
index 0000000..510539d
--- /dev/null
+++ src/oneShot/mmapPerf/mmapPerf.c
@@ -0,0 +1,174 @@
+/* mmapPerf - Test perfomance of mmap. */
+#define _XOPEN_SOURCE 1
+#define _BSD_SOURCE 1
+#include "common.h"
+#include "linefile.h"
+#include "options.h"
+#include "verbose.h"
+#include <math.h>
+#include <time.h>
+#include <float.h>
+#include <sys/mman.h>
+
+void usage()
+/* Explain usage and exit. */
+{
+errAbort(
+  "mmapPerf - Test perfomance of mmap\n"
+  "usage:\n"
+  "   mmapPerf testFileList what outTsv\n"
+  "\n"
+  "  - testFileList is a file contained files to test.  The first is use to warm\n"
+  "    up the system, the remainder are used to run the tests\n"
+  "  - what - label to include in output file\n"
+  "  - outTsv - results written to this TSV\n"
+  "\n"
+  "options:\n"
+  "   -accesses=100000 - number of access per test round\n"
+  "   -random - set madvise MADV_RANDOM on the mapped file range\n"
+  "   -willneed - set madvise MADV_WILLNEED on the mapped file range\n"
+  "   -clear - clear file cache\n"
+  "   -verbose=n - set verbosity\n"
+  );
+}
+
+/* Command line validation table. */
+static struct optionSpec options[] = {
+    {"accesses", OPTION_INT},
+    {"clear", OPTION_BOOLEAN},
+    {"random", OPTION_BOOLEAN},
+    {"willneed", OPTION_BOOLEAN},
+    {NULL, 0}
+};
+
+static double elapsed(clock_t start)
+/* compute elapsed time since start in seconds */
+{
+return ((double)(clock() - start)) / CLOCKS_PER_SEC;
+}
+
+static void clearCaches(void)
+/* clear caches */
+{
+const char *cmd = "sudo /root/clearCaches";
+verbose(1, "Begin file cache clear\n");
+verboseTimeInit();
+if (system(cmd) != 0)
+    errAbort("%s failed", cmd);
+verboseTime(1, "End file cache clear");
+}
+
+static struct slName* loadTestFileList(char *testFileList)
+/* load test file list */
+{
+struct slName *testFiles = NULL;
+struct lineFile *lf = lineFileOpen(testFileList, TRUE);
+char *line;
+while (lineFileNextReal(lf, &line))
+    {
+    if (strlen(line) > 0)
+        slAddHead(&testFiles, slNameNew(line));
+    }
+lineFileClose(&lf);
+slReverse(&testFiles);
+if (slCount(testFiles) <= 1)
+    errAbort("must specify at least two test files");
+
+return testFiles;
+}
+
+static unsigned randAccess(BYTE* fmem,
+                           off_t testFileSize,
+                           int accesses)
+/* randomly access file memory */
+{
+unsigned accum = 0; // make sure not optimized out
+for (int i = 0; i < accesses; i++)
+    {
+    off_t off = round(drand48() * testFileSize);
+    accum |= *(fmem + off);
+    }
+return accum;
+}
+
+static void exerciseFile(char *testFile,
+                         boolean advRandom,
+                         boolean advWillNeed,
+                         int accesses)
+/* run one round of tests on a file */
+{
+off_t testFileSize = fileSize(testFile);
+int fd = open(testFile, 0);
+if (fd < 0)
+    errnoAbort("can't open %s", testFile);
+
+int flags = MAP_SHARED;
+BYTE* fmem = mmap(NULL, testFileSize, PROT_READ, flags, fd, 0);
+if (fmem == MAP_FAILED)
+    errnoAbort("mmap failed: %s", testFile);
+    
+unsigned madvOpts = (advRandom ? MADV_RANDOM : 0)
+    | (advWillNeed ? MADV_WILLNEED : 0);
+
+if (madvOpts != 0)
+    {
+    if (madvise(fmem, testFileSize, madvOpts) < 0)
+        errnoAbort("madvise failed: %s", testFile);
+    }
+
+randAccess(fmem, testFileSize, accesses);
+
+if (munmap(fmem, testFileSize) < 0)
+    errnoAbort("munmap failed");
+if (close(fd) < 0)
+    errnoAbort("can't close");
+}
+
+static void mmapPerf(char *testFileList,
+                     char *what,
+                     char *outTsv,
+                     boolean advRandom, boolean advWillNeed,
+                     int accesses, boolean clear) {
+/* mmapPerf - Test perfomance of mmap. */
+struct slName* testFiles = loadTestFileList(testFileList);
+
+if (clear)
+    clearCaches();
+
+exerciseFile(testFiles->name, advRandom, advWillNeed, 10);
+int trialCnt = 0;
+float totalTime = 0.0, minTime = DBL_MAX, maxTime = 0.0;
+for (struct slName* testFile = testFiles->next; testFile != NULL; testFile = testFile->next)
+    {
+    verbose(1, "Begin trial %d\n", trialCnt);
+    verboseTimeInit();
+    clock_t start = clock();
+    exerciseFile(testFiles->name, advRandom, advWillNeed, accesses);
+    double sec = elapsed(start);
+    totalTime += sec;
+    minTime = min(minTime, sec);
+    maxTime = max(maxTime, sec);
+    verboseTime(1, "End trial %d", trialCnt);
+    trialCnt++;
+    }
+FILE *fh = mustOpen(outTsv, "w");
+fprintf(fh, "what\ttrials\tadvRandom\tadvWillNeed\tclear\taccesses\ttotalTime\tmeanTime\tminTime\tmaxTime\n");
+fprintf(fh, "%s\t%d\t%d\t%d\t%d\t%d\t%0.2f\t%0.2f\t%0.2f\t%0.2f\n", what,
+        trialCnt, advRandom, advWillNeed, clear, accesses,
+        totalTime, totalTime / trialCnt, minTime, maxTime);
+carefulClose(&fh);
+}
+
+int main(int argc, char *argv[])
+/* Process command line. */
+{
+optionInit(&argc, argv, options);
+if (argc != 4)
+    usage();
+mmapPerf(argv[1], argv[2], argv[3],
+         optionExists("random"),
+         optionExists("willneed"),
+         optionInt("accesss", 100000),
+         optionExists("clear"));
+return 0;
+}