Bending the CLOS Mop for Java-Style Single Dispatch
5 days ago
- #Performance Optimization
- #Common Lisp
- #Clojure
- The author encountered performance issues when running Clojure on OpenLDK, a Common Lisp JVM, due to CLOS's multi-method dispatch being inefficient for Java's single-dispatch model.
- The solution involved customizing the CLOS Meta-Object Protocol (MOP) to replace the dispatch machinery for Java methods with a single-dispatch model, significantly improving performance.
- A new metaclass, `java-generic-function`, was created with hash-table caches for dispatch and `invokespecial` calls, along with a lock for thread safety.
- The custom discriminating function for `java-generic-function` uses a hash-table lookup for O(1) dispatch after the first call for each receiver class.
- The biggest performance win came from intercepting PCL's `update-dfun` to clear caches instead of rebuilding the discrimination net, reducing the overhead during Clojure bootstrap.
- Pre-creating hot generic functions like `<init>()` and `clone()` with the `java-generic-function` metaclass ensured fast dispatch from the start.
- A separate cache was added for Java's `invokespecial` instruction, which is used for constructor chaining and `super` calls, further optimizing performance.
- The changes reduced Clojure bootstrap time from 2 hours 45 minutes to 2 minutes 40 seconds, demonstrating the effectiveness of the MOP customization.
- The solution is concise (120 lines of code) and minimally invasive, leveraging the MOP as intended without modifying SBCL or the runtime.