JavaScript in a browser can be at most three times slower than HotSpot when computing five or ten thousands of prime numbers
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Tue, 26 Jan 2016 04:36:23 +0100
changeset 18604ce38f21f4cd
parent 1859 727d3be7e03c
child 1861 433856f897dd
JavaScript in a browser can be at most three times slower than HotSpot when computing five or ten thousands of prime numbers
benchmarks/sieve/pom.xml
benchmarks/sieve/src/test/java/org/apidesign/benchmark/sieve/SieveTest.java
launcher/api/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java
launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java
launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/Console.java
launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java
launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java
rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/Compare.java
rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java
rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java
     1.1 --- a/benchmarks/sieve/pom.xml	Mon Jan 25 08:16:33 2016 +0100
     1.2 +++ b/benchmarks/sieve/pom.xml	Tue Jan 26 04:36:23 2016 +0100
     1.3 @@ -38,6 +38,15 @@
     1.4                </configuration>
     1.5            </plugin>      
     1.6            <plugin>
     1.7 +              <groupId>org.apache.maven.plugins</groupId>
     1.8 +              <artifactId>maven-surefire-plugin</artifactId>
     1.9 +              <configuration>
    1.10 +                  <systemPropertyVariables>
    1.11 +                      <vmtest.js>brwsr</vmtest.js>
    1.12 +                  </systemPropertyVariables>
    1.13 +              </configuration>
    1.14 +          </plugin>
    1.15 +          <plugin>
    1.16                <groupId>org.codehaus.mojo</groupId>
    1.17                <artifactId>xml-maven-plugin</artifactId>
    1.18                <version>1.0</version>
     2.1 --- a/benchmarks/sieve/src/test/java/org/apidesign/benchmark/sieve/SieveTest.java	Mon Jan 25 08:16:33 2016 +0100
     2.2 +++ b/benchmarks/sieve/src/test/java/org/apidesign/benchmark/sieve/SieveTest.java	Tue Jan 26 04:36:23 2016 +0100
     2.3 @@ -36,7 +36,7 @@
     2.4          return (int) System.currentTimeMillis();
     2.5      }
     2.6  
     2.7 -    @Compare(scripting = false) 
     2.8 +    @Compare
     2.9      public int oneThousand() throws IOException {
    2.10          SieveTest sieve = new SieveTest();
    2.11          int now = time();
    2.12 @@ -46,7 +46,7 @@
    2.13          return res;
    2.14      }
    2.15  
    2.16 -    @Compare(scripting = false)
    2.17 +    @Compare(slowdown = 3.0)
    2.18      public int fiveThousand() throws IOException {
    2.19          SieveTest sieve = new SieveTest();
    2.20          int now = time();
    2.21 @@ -55,6 +55,16 @@
    2.22          log("oneThousand in " + took + " ms");
    2.23          return res;
    2.24      }
    2.25 +
    2.26 +    @Compare(slowdown = 3.0)
    2.27 +    public int tenThousand() throws IOException {
    2.28 +        SieveTest sieve = new SieveTest();
    2.29 +        int now = time();
    2.30 +        int res = sieve.compute(10000);
    2.31 +        int took = time() - now;
    2.32 +        log("oneThousand in " + took + " ms");
    2.33 +        return res;
    2.34 +    }
    2.35      
    2.36      @Factory
    2.37      public static Object[] create() {
     3.1 --- a/launcher/api/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java	Mon Jan 25 08:16:33 2016 +0100
     3.2 +++ b/launcher/api/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java	Tue Jan 26 04:36:23 2016 +0100
     3.3 @@ -19,7 +19,6 @@
     3.4  
     3.5  import java.io.IOException;
     3.6  import java.io.InputStream;
     3.7 -import java.net.URI;
     3.8  import java.util.ArrayList;
     3.9  import java.util.List;
    3.10  import java.util.concurrent.CountDownLatch;
    3.11 @@ -38,6 +37,7 @@
    3.12      private Throwable exception;
    3.13      String html;
    3.14      final List<Resource> resources = new ArrayList<>();
    3.15 +    private int time;
    3.16  
    3.17      InvocationContext(Launcher launcher, Class<?> clazz, String methodName) {
    3.18          this.launcher = launcher;
    3.19 @@ -65,11 +65,27 @@
    3.20      
    3.21      /** Invokes the associated method. 
    3.22       * @return the textual result of the invocation
    3.23 +     * @throws java.io.IOException if execution fails
    3.24       */
    3.25      public String invoke() throws IOException {
    3.26          launcher.runMethod(this);
    3.27          return toString();
    3.28      }
    3.29 +
    3.30 +    /** Invokes the associated method.
    3.31 +     * @param time one element array to store the length of the invocation
    3.32 +     *    - can be <code>null</code>
    3.33 +     * @return the textual result of the invocation
    3.34 +     * @throws java.io.IOException if execution fails
    3.35 +     * @since 0.20
    3.36 +     */
    3.37 +    public String invoke(int[] time) throws IOException {
    3.38 +        launcher.runMethod(this);
    3.39 +        if (time != null) {
    3.40 +            time[0] = this.time;
    3.41 +        }
    3.42 +        return toString();
    3.43 +    }
    3.44      
    3.45      /** Obtains textual result of the invocation.
    3.46       * @return text representing the exception or result value
    3.47 @@ -90,7 +106,8 @@
    3.48          wait.await(timeOut, TimeUnit.MILLISECONDS);
    3.49      }
    3.50      
    3.51 -    void result(String r, Throwable e) {
    3.52 +    void result(String r, int time, Throwable e) {
    3.53 +        this.time = time;
    3.54          this.result = r;
    3.55          this.exception = e;
    3.56          wait.countDown();
     4.1 --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java	Mon Jan 25 08:16:33 2016 +0100
     4.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java	Tue Jan 26 04:36:23 2016 +0100
     4.3 @@ -337,10 +337,20 @@
     4.4              public void service(Request request, Response response) throws Exception {
     4.5                  String id = request.getParameter("request");
     4.6                  String value = request.getParameter("result");
     4.7 +                String timeText = request.getParameter("time");
     4.8                  if (value != null && value.indexOf((char)0xC5) != -1) {
     4.9                      value = toUTF8(value);
    4.10                  }
    4.11 -
    4.12 +                int time;
    4.13 +                if (timeText != null) {
    4.14 +                    try {
    4.15 +                        time = (int) Double.parseDouble(timeText);
    4.16 +                    } catch (NumberFormatException numberFormatException) {
    4.17 +                        time = 0;
    4.18 +                    }
    4.19 +                } else {
    4.20 +                    time = 0;
    4.21 +                }
    4.22  
    4.23                  InvocationContext mi = null;
    4.24                  int caseNmbr = -1;
    4.25 @@ -349,7 +359,7 @@
    4.26                      LOG.log(Level.INFO, "Received result for case {0} = {1}", new Object[]{id, value});
    4.27                      value = decodeURL(value);
    4.28                      int indx = Integer.parseInt(id);
    4.29 -                    cases.get(indx).result(value, null);
    4.30 +                    cases.get(indx).result(value, time, null);
    4.31                      if (++indx < cases.size()) {
    4.32                          mi = cases.get(indx);
    4.33                          LOG.log(Level.INFO, "Re-executing case {0}", indx);
     5.1 --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/Console.java	Mon Jan 25 08:16:33 2016 +0100
     5.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/Console.java	Tue Jan 26 04:36:23 2016 +0100
     5.3 @@ -43,7 +43,10 @@
     5.4      private static native void setAttr(String id, String attr, Object value);
     5.5  
     5.6      @JavaScriptBody(args = { "elem", "attr", "value" }, body = "elem[attr] = value;")
     5.7 -    private static native void setAttr(Object id, String attr, Object value);
     5.8 +    private static native void setAttr(Object elem, String attr, Object value);
     5.9 +
    5.10 +    @JavaScriptBody(args = {}, body = "return new Date().getTime()")
    5.11 +    private static native double getTime();
    5.12      
    5.13      private static void closeWindow() {}
    5.14  
    5.15 @@ -175,9 +178,11 @@
    5.16                      log("Processing \"" + arr[0] + "\" for " + retries + " time");
    5.17                  }
    5.18                  Object result = retries++ >= 100 ? "java.lang.InterruptedException:timeout(" + retries + ")" : c.runTest();
    5.19 +                String reply = "?request=" + c.getRequestId() + "&time=" + c.time + "&result=" + result;
    5.20 +                log("Sending back: ..." + reply);
    5.21                  finishTest(c, result);
    5.22                  
    5.23 -                String u = url + "?request=" + c.getRequestId() + "&result=" + result;
    5.24 +                String u = url + reply;
    5.25                  new Request(url, u);
    5.26              } catch (Exception ex) {
    5.27                  if (ex instanceof InterruptedException) {
    5.28 @@ -264,6 +269,7 @@
    5.29      
    5.30      private static final class Case {
    5.31          private final Object data;
    5.32 +        private double time;
    5.33          private Object inst;
    5.34  
    5.35          private Case(Object data) {
    5.36 @@ -315,8 +321,8 @@
    5.37              Object result = invokeMethod(this.getClassName(), this.getMethodName());
    5.38              setAttr("bck2brwsr.fragment", "innerHTML", "");
    5.39              log("Result: " + result);
    5.40 +            log("Time: " + time + " ms");
    5.41              result = encodeURL("" + result);
    5.42 -            log("Sending back: ...?request=" + this.getRequestId() + "&result=" + result);
    5.43              return result;
    5.44          }
    5.45  
    5.46 @@ -340,7 +346,11 @@
    5.47                          if (inst == null) {
    5.48                              inst = c.newInstance();
    5.49                          }
    5.50 +                        double now = getTime();
    5.51                          res = found.invoke(inst);
    5.52 +                        double took = Math.round(getTime() - now);
    5.53 +                        time += took;
    5.54 +                        log("Execution took " + took + " ms");
    5.55                      }
    5.56                  } catch (Throwable ex) {
    5.57                      if (ex instanceof InvocationTargetException) {
     6.1 --- a/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java	Mon Jan 25 08:16:33 2016 +0100
     6.2 +++ b/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java	Tue Jan 26 04:36:23 2016 +0100
     6.3 @@ -58,9 +58,9 @@
     6.4                  mi.clazz.getName(), mi.methodName).toString();
     6.5              time = System.currentTimeMillis() - time;
     6.6              LOG.log(Level.FINE, "Resut of {0}.{1} = {2} in {3} ms", new Object[]{mi.clazz.getName(), mi.methodName, res, time});
     6.7 -            mi.result(res, null);
     6.8 +            mi.result(res, (int)time, null);
     6.9          } catch (ScriptException | NoSuchMethodException ex) {
    6.10 -            mi.result(null, ex);
    6.11 +            mi.result(null, -1, ex);
    6.12          }
    6.13          return mi;
    6.14      }
     7.1 --- a/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java	Mon Jan 25 08:16:33 2016 +0100
     7.2 +++ b/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java	Tue Jan 26 04:36:23 2016 +0100
     7.3 @@ -53,6 +53,9 @@
     7.4          "elem[attr] = value;")
     7.5      private static native void setAttr(Object id, String attr, Object value);
     7.6      
     7.7 +    @net.java.html.js.JavaScriptBody(args = {}, body = "return new Date().getTime()")
     7.8 +    private static native double getTime();
     7.9 +
    7.10      @net.java.html.js.JavaScriptBody(args = {  }, body = 
    7.11          "var a = document.createElement('a');"
    7.12        + "a.innerHTML = 'Cancel: closing in 10s...';\n"
    7.13 @@ -180,9 +183,11 @@
    7.14                      log("Processing \"" + arr[0] + "\" for " + retries + " time");
    7.15                  }
    7.16                  Object result = retries++ >= 100 ? "java.lang.InterruptedException:timeout(" + retries + ")" : c.runTest();
    7.17 +                String reply = "?request=" + c.getRequestId() + "&time=" + c.time + "&result=" + result;
    7.18 +                log("Sending back: ..." + reply);
    7.19                  finishTest(c, result);
    7.20                  
    7.21 -                String u = url + "?request=" + c.getRequestId() + "&result=" + result;
    7.22 +                String u = url + reply;
    7.23                  new Request(url, u);
    7.24              } catch (Exception ex) {
    7.25                  if (ex instanceof InterruptedException) {
    7.26 @@ -285,6 +290,7 @@
    7.27      private static final class Case {
    7.28          private final Object data;
    7.29          private Object inst;
    7.30 +        private double time;
    7.31  
    7.32          private Case(Object data) {
    7.33              this.data = data;
    7.34 @@ -332,7 +338,6 @@
    7.35              setAttr("bck2brwsr.fragment", "innerHTML", "");
    7.36              log("Result: " + result);
    7.37              result = encodeURL("" + result);
    7.38 -            log("Sending back: ...?request=" + this.getRequestId() + "&result=" + result);
    7.39              return result;
    7.40          }
    7.41  
    7.42 @@ -350,14 +355,19 @@
    7.43              Object res;
    7.44              if (found != null) {
    7.45                  try {
    7.46 +                    double now;
    7.47                      if ((found.getModifiers() & Modifier.STATIC) != 0) {
    7.48 +                        now = getTime();
    7.49                          res = found.invoke(null);
    7.50                      } else {
    7.51                          if (inst == null) {
    7.52                              inst = c.newInstance();
    7.53                          }
    7.54 +                        now = getTime();
    7.55                          res = found.invoke(inst);
    7.56                      }
    7.57 +                    double took = Math.round((float)(getTime() - now));
    7.58 +                    time += took;
    7.59                  } catch (Throwable ex) {
    7.60                      if (ex instanceof InvocationTargetException) {
    7.61                          ex = ((InvocationTargetException) ex).getTargetException();
     8.1 --- a/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/Compare.java	Mon Jan 25 08:16:33 2016 +0100
     8.2 +++ b/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/Compare.java	Tue Jan 26 04:36:23 2016 +0100
     8.3 @@ -42,4 +42,11 @@
     8.4       * @return 
     8.5       */
     8.6      boolean scripting() default true;
     8.7 +
     8.8 +    /** Compare (or not) execution times. If the value is specified and
     8.9 +     * bigger than zero, then the fastest and slowest execution time is
    8.10 +     * compared and if the ratio between them is higher, the compare fails.
    8.11 +     * @return ratio (e.g. 2x, 3x, 3.5x) between execution times
    8.12 +     */
    8.13 +    double slowdown() default -1;
    8.14  }
     9.1 --- a/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java	Mon Jan 25 08:16:33 2016 +0100
     9.2 +++ b/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java	Tue Jan 26 04:36:23 2016 +0100
     9.3 @@ -47,6 +47,7 @@
     9.4      private final Http.Resource[] http;
     9.5      private final InvocationContext c;
     9.6      Object value;
     9.7 +    int time;
     9.8  
     9.9      Bck2BrwsrCase(Method m, String type, Launcher l, boolean fail, HtmlFragment html, Http.Resource[] http) {
    9.10          this.l = l;
    9.11 @@ -75,8 +76,10 @@
    9.12                      }
    9.13                  }
    9.14              }
    9.15 -            String res = c.invoke();
    9.16 -            value = res;
    9.17 +            int[] time = { 0 };
    9.18 +            String res = c.invoke(time);
    9.19 +            this.value = res;
    9.20 +            this.time = time[0];
    9.21              if (fail) {
    9.22                  int idx = res == null ? -1 : res.indexOf(':');
    9.23                  if (idx >= 0) {
    9.24 @@ -111,7 +114,9 @@
    9.25              }
    9.26          } else {
    9.27              try {
    9.28 +                long now = System.currentTimeMillis();
    9.29                  value = m.invoke(m.getDeclaringClass().newInstance());
    9.30 +                time = (int) (System.currentTimeMillis() - now);
    9.31              } catch (InvocationTargetException ex) {
    9.32                  Throwable t = ex.getTargetException();
    9.33                  value = t.getClass().getName() + ":" + t.getMessage();
    10.1 --- a/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java	Mon Jan 25 08:16:33 2016 +0100
    10.2 +++ b/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java	Tue Jan 26 04:36:23 2016 +0100
    10.3 @@ -40,11 +40,13 @@
    10.4  public final class CompareCase implements ITest {
    10.5      private final Bck2BrwsrCase first, second;
    10.6      private final Method m;
    10.7 +    private final double slowdown;
    10.8      
    10.9 -    private CompareCase(Method m, Bck2BrwsrCase first, Bck2BrwsrCase second) {
   10.10 +    private CompareCase(Method m, Bck2BrwsrCase first, Bck2BrwsrCase second, double slowdown) {
   10.11          this.first = first;
   10.12          this.second = second;
   10.13          this.m = m;
   10.14 +        this.slowdown = slowdown;
   10.15      }
   10.16  
   10.17      /** Inspects <code>clazz</code> and for each {@lik Compare} method creates
   10.18 @@ -120,6 +122,20 @@
   10.19              Bck2BrwsrCase.dumpJS(sb, second);
   10.20              throw new AssertionError(sb.toString());
   10.21          }
   10.22 +        if (slowdown > 0.0) {
   10.23 +            Bck2BrwsrCase slow;
   10.24 +            Bck2BrwsrCase fast;
   10.25 +            if (first.time >= second.time) {
   10.26 +                slow = second;
   10.27 +                fast = first;
   10.28 +            } else {
   10.29 +                fast = second;
   10.30 +                slow = first;
   10.31 +            }
   10.32 +            if (slow.time * slowdown < fast.time) {
   10.33 +                Assert.fail("Too slow " + slow.getTestName() + " took " + slow.time + " ms vs. " + fast.time + " ms of " + fast.getTestName());
   10.34 +            }
   10.35 +        }
   10.36      }
   10.37      
   10.38      /** Test name.
   10.39 @@ -135,19 +151,24 @@
   10.40          if (c == null) {
   10.41              return;
   10.42          }
   10.43 +        String slowdownOverride = System.getProperty("vmtest.slowdown");
   10.44          final Bck2BrwsrCase real = new Bck2BrwsrCase(m, "Java", null, false, null, null);
   10.45          ret.add(real);
   10.46 +        double slowdown = c.slowdown();
   10.47 +        if (slowdown > 0.0 && slowdownOverride != null) {
   10.48 +            slowdown = Double.parseDouble(slowdownOverride);
   10.49 +        }
   10.50          if (c.scripting()) {
   10.51              final Bck2BrwsrCase js = new Bck2BrwsrCase(m, "JavaScript", l.javaScript(), false, null, null);
   10.52              ret.add(js);
   10.53 -            ret.add(new CompareCase(m, real, js));
   10.54 +            ret.add(new CompareCase(m, real, js, slowdown));
   10.55          }
   10.56          for (String b : brwsr) {
   10.57              final Launcher s = l.brwsr(b);
   10.58              ret.add(s);
   10.59              final Bck2BrwsrCase cse = new Bck2BrwsrCase(m, b, s, false, null, null);
   10.60              ret.add(cse);
   10.61 -            ret.add(new CompareCase(m, real, cse));
   10.62 +            ret.add(new CompareCase(m, real, cse, slowdown));
   10.63          }
   10.64      }
   10.65      private static void registerBrwsrCases(Class<? extends Annotation> brwsrTest, Method m, final LaunchSetup l, List<Object> ret, String[] brwsr) {