major
minor
major
minor
When the tl:KnowledgeBase creates a tl:TLObject—for example, as a result of a search—it must find the appropriate implementation class for its tl:TLClass.
Improvement
The mapping from implementation class to tl:TLClass should be cached. This eliminates the need to search for the Java class again for every TLObject.
Implementation
A cache has been built into tl:DynamicBinding. See: DynamicBinding._implementationClasses. The cache stores the mapping of the tl:TLID of a tl:TLType to the Java Class object of the tl:TLObject implementation. As a result, slightly more information is stored than just the resolution of the fully qualified class name into the Class object, which further improves performance. On the other hand, this makes generalization more difficult. However, there are currently no concrete plans to cache class resolution in other places anyway.
Background
As part of #26910, it was determined that this area is worth optimizing. When sending a changeset of 300,000 objects, this section accounted for two-thirds of the remaining runtime after other optimizations had been applied.
Example Stack Trace
at com.top_logic.knowledge.wrap.binding.DynamicBinding.findImplClass(DynamicBinding.java:152) at com.top_logic.knowledge.wrap.binding.DynamicBinding.createBinding(DynamicBinding.java:133) at com.top_logic.knowledge.wrap.ImplementationFactory.createBinding(ImplementationFactory.java:53) at com.top_logic.knowledge.service.db2.KnowledgeItemImpl.createWrapper(KnowledgeItemImpl.java:46) at com.top_logic.knowledge.service.db2.WrappedKnowledgeItem.initWrapper(WrappedKnowledgeItem.java:28) at com.top_logic.knowledge.service.db2.DBKnowledgeItem.onLoad(DBKnowledgeItem.java:259) at com.top_logic.knowledge.service.db2.DBKnowledgeBase.createItem(DBKnowledgeBase.java:5158) at com.top_logic.knowledge.service.db2.DBKnowledgeBase.findOrCreateItem(DBKnowledgeBase.java:5122) at com.top_logic.knowledge.service.db2.DBKnowledgeBase.findOrCreateItem(DBKnowledgeBase.java:5090) at com.top_logic.knowledge.service.db2.MonomorphicSearch$FullObjectResult.findNext(MonomorphicSearch.java:321) at com.top_logic.basic.sql.ResultSetBasedIterator.findNext(ResultSetBasedIterator.java:44) at com.top_logic.basic.col.CloseableIteratorBase.hasNext(CloseableIteratorBase.java:31) at com.top_logic.basic.col.CloseableIteratorAdapter.hasNext(CloseableIteratorAdapter.java:29) at com.top_logic.knowledge.service.db2.DBKnowledgeBase.toList(DBKnowledgeBase.java:1926) at com.top_logic.knowledge.service.db2.DBKnowledgeBase.search(DBKnowledgeBase.java:1912) at com.top_logic.knowledge.service.BulkIdLoad.resolveIdentifiers(BulkIdLoad.java:186) at com.top_logic.knowledge.service.BulkIdLoad.loadUncachedInRevision(BulkIdLoad.java:126) at com.top_logic.kafka.knowledge.service.exporter.TypeFilterRewriter.resolveCallbacks(TypeFilterRewriter.java:161) at com.top_logic.kafka.knowledge.service.exporter.TypeFilterRewriter.rewrite(TypeFilterRewriter.java:149)
Test
TestClassCaching checks whether ConcurrentHashMap.computeIfAbsent(...) is faster than Class.forName(...). Currently, this is clearly the case. The latter takes more than 10 times as long. (JDK 11, Linux)
Limitations: The map has only one entry, and exactly this entry is always retrieved. However, the performance of `get(key)` for maps does not depend heavily on the map’s fill level. The general overhead involved in retrieving an entry, as well as the synchronization required by **`Concurrent`**`HashMap`, should be the deciding factors here. Additionally, the actual cache stores more than just this mapping and should therefore save even more time.