Modify kilobench to run each sample in its own process
authorjoshualitt <joshualitt@chromium.org>
Fri, 15 Jan 2016 20:07:39 +0000 (12:07 -0800)
committerCommit bot <commit-bot@chromium.org>
Fri, 15 Jan 2016 20:07:39 +0000 (12:07 -0800)
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1594553003

Review URL: https://codereview.chromium.org/1594553003

gyp/most.gyp
tools/kilobench/kilobench.cpp

index ba7b3c43c6a42911931e80498aa46772209c5cc8..1a2537a5f20c827c5e104d71c9c0ce714aceb71f 100644 (file)
             'kilobench.gyp:kilobench',
           ]
         }],
+        [ 'skia_os != "android" and skia_os != "linux"', {
+          'dependencies!': [
+            'kilobench.gyp:kilobench',
+          ],
+        }],
         [ 'skia_gpu == 0 or skia_os == "android"', {
           'dependencies!': [
             'example.gyp:HelloWorld',
index 438e582324fb2e8c97f1da061e7d9a3f8760d870..8c844f47e0169ac26f72cae33d22b03fb52d8e50 100644 (file)
@@ -8,6 +8,7 @@
 #include "GrCaps.h"
 #include "GrContextFactory.h"
 #include "Benchmark.h"
+#include "ResultsWriter.h"
 #include "SkCommandLineFlags.h"
 #include "SkOSFile.h"
 #include "SkStream.h"
 #include "VisualSKPBench.h"
 #include "gl/GrGLDefines.h"
 
+// posix only for now
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
 /*
  * This is an experimental GPU only benchmarking program.  The initial implementation will only
  * support SKPs.
@@ -63,6 +69,11 @@ DEFINE_int32(loops, kDefaultLoops, loops_help_txt().c_str());
 DEFINE_double(gpuMs, 5, "Target bench time in millseconds for GPU.");
 DEFINE_string2(writePath, w, "", "If set, write bitmaps here as .pngs.");
 
+static SkString humanize(double ms) {
+    return HumanizeMs(ms);
+}
+#define HUMANIZE(ms) humanize(ms).c_str()
+
 namespace kilobench {
 class BenchmarkStream {
 public:
@@ -322,76 +333,135 @@ static int setup_gpu_bench(GPUTarget* target, Benchmark* bench, int maxGpuFrameL
     return loops;
 }
 
-static SkString humanize(double ms) {
-    return HumanizeMs(ms);
-}
-#define HUMANIZE(ms) humanize(ms).c_str()
+struct AutoSetupContextBenchAndTarget {
+    AutoSetupContextBenchAndTarget(Benchmark* bench) : fBenchmark(bench) {
+        GrContextOptions grContextOpts;
+        fCtxFactory.reset(new GrContextFactory(grContextOpts));
 
-void benchmark_inner_loop(Benchmark* bench, GrContextFactory* ctxFactory) {
-    SkTArray<double> samples;
-    GPUTarget target;
-    SkAssertResult(target.init(bench, ctxFactory, false,
-                               GrContextFactory::kNative_GLContextType,
-                               GrContextFactory::kNone_GLContextOptions, 0));
+        SkAssertResult(fTarget.init(bench, fCtxFactory, false,
+                                    GrContextFactory::kNative_GLContextType,
+                                    GrContextFactory::kNone_GLContextOptions, 0));
 
-    SkCanvas* canvas = target.getCanvas();
-    target.setup();
+        fCanvas = fTarget.getCanvas();
+        fTarget.setup();
 
-    bench->perCanvasPreDraw(canvas);
-    int maxFrameLag;
-    target.needsFrameTiming(&maxFrameLag);
-    int loops = setup_gpu_bench(&target, bench, maxFrameLag);
+        bench->perCanvasPreDraw(fCanvas);
+        fTarget.needsFrameTiming(&fMaxFrameLag);
+    }
+
+    int getLoops() { return setup_gpu_bench(&fTarget, fBenchmark, fMaxFrameLag); }
 
-    samples.reset(FLAGS_samples);
-    for (int s = 0; s < FLAGS_samples; s++) {
-        samples[s] = time(loops, bench, &target) / loops;
+    double timeSample(int loops) {
+        for (int i = 0; i < fMaxFrameLag; i++) {
+            time(loops, fBenchmark, &fTarget);
+        }
+
+        return time(loops, fBenchmark, &fTarget) / loops;
     }
+    void teardownBench() { fBenchmark->perCanvasPostDraw(fCanvas); }
+
+    SkAutoTDelete<GrContextFactory> fCtxFactory;
+    GPUTarget fTarget;
+    SkCanvas* fCanvas;
+    Benchmark* fBenchmark;
+    int fMaxFrameLag;
+};
 
-    bench->perCanvasPostDraw(canvas);
-
-    Stats stats(samples);
-    const double stddev_percent = 100 * sqrt(stats.var) / stats.mean;
-    SkDebugf("%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\t%s\n"
-            , loops
-            , HUMANIZE(stats.min)
-            , HUMANIZE(stats.median)
-            , HUMANIZE(stats.mean)
-            , HUMANIZE(stats.max)
-            , stddev_percent
-            , stats.plot.c_str()
-            , "gpu"
-            , bench->getUniqueName()
-            );
+int setup_loops(Benchmark* bench) {
+    AutoSetupContextBenchAndTarget ascbt(bench);
+    int loops = ascbt.getLoops();
+    ascbt.teardownBench();
 
     if (!FLAGS_writePath.isEmpty() && FLAGS_writePath[0]) {
         SkString pngFilename = SkOSPath::Join(FLAGS_writePath[0], "gpu");
         pngFilename = SkOSPath::Join(pngFilename.c_str(), bench->getUniqueName());
         pngFilename.append(".png");
-        write_canvas_png(&target, pngFilename);
+        write_canvas_png(&ascbt.fTarget, pngFilename);
     }
+    return loops;
 }
 
-} // namespace kilobench
+double time_sample(Benchmark* bench, int loops) {
+    AutoSetupContextBenchAndTarget ascbt(bench);
+    double sample = ascbt.timeSample(loops);
+    ascbt.teardownBench();
 
-int kilobench_main() {
-    SkAutoTDelete<GrContextFactory> ctxFactory;
+    return sample;
+}
 
-    GrContextOptions grContextOpts;
-    ctxFactory.reset(new GrContextFactory(grContextOpts));
+} // namespace kilobench
+
+static const int kOutResultSize = 1024;
 
+int kilobench_main() {
     kilobench::BenchmarkStream benchStream;
 
     SkDebugf("loops\tmin\tmedian\tmean\tmax\tstddev\t%-*s\tconfig\tbench\n",
              FLAGS_samples, "samples");
 
+    int descriptors[2];
+    if (pipe(descriptors) != 0) {
+        SkFAIL("Failed to open a pipe\n");
+    }
+
     while (Benchmark* b = benchStream.next()) {
         SkAutoTDelete<Benchmark> bench(b);
-        kilobench::benchmark_inner_loop(bench.get(), ctxFactory.get());
-    }
 
-    // Make sure we clean up the global GrContextFactory here, otherwise we might race with the
-    // SkEventTracer destructor
-    ctxFactory.reset(nullptr);
+        int loops;
+        SkTArray<double> samples;
+        for (int i = 0; i < FLAGS_samples + 1; i++) {
+            // We fork off a new process to setup the grcontext and run the test while we wait
+            int childPid = fork();
+            if (childPid > 0) {
+                char result[kOutResultSize];
+                if (read(descriptors[0], result, kOutResultSize) < 0) {
+                     SkFAIL("Failed to read from pipe\n");
+                }
+
+                // if samples == 0 then parse # of loops
+                // else parse float
+                if (i == 0) {
+                    sscanf(result, "%d", &loops);
+                } else {
+                    sscanf(result, "%lf", &samples.push_back());
+                }
+
+                // wait until exit
+                int status;
+                waitpid(childPid, &status, 0);
+            } else if (0 == childPid) {
+                char result[kOutResultSize];
+                if (i == 0) {
+                    sprintf(result, "%d", kilobench::setup_loops(bench));
+                } else {
+                    sprintf(result, "%lf", kilobench::time_sample(bench, loops));
+                }
+
+                // Make sure to write the null terminator
+                if (write(descriptors[1], result, strlen(result) + 1) < 0) {
+                    SkFAIL("Failed to write to pipe\n");
+                }
+                return 0;
+            } else {
+                SkFAIL("Fork failed\n");
+            }
+        }
+
+        Stats stats(samples);
+        const double stddev_percent = 100 * sqrt(stats.var) / stats.mean;
+        SkDebugf("%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\t%s\n"
+                , loops
+                , HUMANIZE(stats.min)
+                , HUMANIZE(stats.median)
+                , HUMANIZE(stats.mean)
+                , HUMANIZE(stats.max)
+                , stddev_percent
+                , stats.plot.c_str()
+                , "gpu"
+                , bench->getUniqueName()
+                );
+
+    }
     return 0;
 }