Performance

Overview

Given bindgen is just method calls, it is fast.

This was not an explicit design goal, but instead a benefit that comes from the primary goal of using type-safe, compile-checked constructs.

Stats

Calling the expression parent.name 1,000,000 times gives the follow stats:

          ognl cached:         1124535381ns
       click proputil:          313962668ns
          new binding:          238833707ns
     existing binding:           23681850ns
    stateless binding:           28652874ns

So, in a tight loop like rendering a table, bindgen is 97.5% faster than OGNL.

The test code:

    public class BindgenPerfTest extends TestCase {

      private static final int loops = 1000000;

      public void testOgnlWithCachingByClickPropertyUtils() throws Exception {
        Parent p = new Parent("p");
        Child c = new Child(p, "c");

        Map<String, String> context = new HashMap<String, String>
        PropertyUtils.getValueOgnl(c, "parent.name", context); // Let it cache  
        long start = System.nanoTime();
        for (int i = 0; i < BindgenPerfTest.loops; i++) {
          Assert.assertEquals("p", PropertyUtils.getValueOgnl(c, "parent.name", context));
        }
        this.stop("ognl cached", start);
      }

      public void testClickPropertyUtils() {
        Parent p = new Parent("p");
        Child c = new Child(p, "c");

        Map< ?> cache = new HashMap<String, String>
        PropertyUtils.getValue(c, "parent.name", cache); // Let it cache  
        long start = System.nanoTime();
        for (int i = 0; i < BindgenPerfTest.loops; i++) {
          Assert.assertEquals("p", PropertyUtils.getValue(c, "parent.name", cache));
        }
        this.stop("click proputil", start);
      }

      public void testBindgenNewBindingEachTime() {
        Parent p = new Parent("p");
        Child c = new Child(p, "c");

        long start = System.nanoTime();
        for (int i = 0; i < BindgenPerfTest.loops; i++) {
          Assert.assertEquals("p", new ChildBinding(c).parent().name().get());
        }
        this.stop("new binding", start);
      }

      public void testBindgenReuseExistingBinding() {
        Parent p = new Parent("p");
        Child c = new Child(p, "c");

        ChildBinding cb = new ChildBinding();
        Binding<String> b = cb.parent().name();
        long start = System.nanoTime();
        for (int i = 0; i < BindgenPerfTest.loops; i++) {
          cb.set(c);
          Assert.assertEquals("p", b.get());
        }
        this.stop("existing binding", start);
      }

      public void testBindgenReuseStatelessBinding() {
        Parent p = new Parent("p");
        Child c = new Child(p, "c");

        ChildBinding cb = new ChildBinding();
        BindingRoot<Child, String> b = cb.parent().name();
        long start = System.nanoTime();
        for (int i = 0; i < BindgenPerfTest.loops; i++) {
          Assert.assertEquals("p", b.getWithRoot(c));
        }
        this.stop("stateless binding", start);
      }

      private void stop(String description, long start) {
        System.out.println(StringUtils.leftPad(description, 18) + ": " + StringUtils.leftPad((System.nanoTime() - start) + "ns", 20));
      }
    }

This should be a reproducible Japex-driven test, but is not yet.