JavaScript in a browser can be at most three times slower than HotSpot when computing five or ten thousands of prime numbers
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) {