[
https://issues.apache.org/jira/browse/OWB-1464?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18094032#comment-18094032
]
Thomas Andraschko commented on OWB-1464:
----------------------------------------
Subject: [PATCH] Improve performance
---
Index:
webbeans-el22/src/main/java/org/apache/webbeans/el22/WebBeansELResolver.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git
a/webbeans-el22/src/main/java/org/apache/webbeans/el22/WebBeansELResolver.java
b/webbeans-el22/src/main/java/org/apache/webbeans/el22/WebBeansELResolver.java
---
a/webbeans-el22/src/main/java/org/apache/webbeans/el22/WebBeansELResolver.java
(revision aa59630b4458f8aff9d50c375279935e45f69a02)
+++
b/webbeans-el22/src/main/java/org/apache/webbeans/el22/WebBeansELResolver.java
(date 1783349005867)
@@ -30,9 +30,13 @@
import org.apache.webbeans.el.ELContextStore;
import java.lang.reflect.Type;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.NavigableMap;
import java.util.Set;
-import java.util.concurrent.CopyOnWriteArraySet;
-import java.util.stream.Collectors;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
/**
@@ -55,12 +59,28 @@
public class WebBeansELResolver extends ELResolver
{
private final WebBeansContext webBeansContext;
- private Set<String> dotNamedBeansNegativeCache;
+
+ /**
+ * beanName (or dotted-prefix) -> {@code true}, for names which are known
to never resolve
+ * to a dot-named {@link Bean}. Backed by a {@link ConcurrentHashMap}
instead of a
+ * {@link java.util.concurrent.CopyOnWriteArraySet} because misses can be
caused by
+ * arbitrary/high-cardinality EL identifiers (e.g. per-row expressions),
which would
+ * otherwise degrade to an O(n) array copy on every single new entry.
+ */
+ private final Map<String, Boolean> dotNamedBeansNegativeCache = new
ConcurrentHashMap<>();
+
+ /**
+ * Lazily built, immutable-after-publish index of all {@link Bean}s whose
name contains a dot,
+ * keyed by their full name. Only such beans can ever be candidates for
+ * {@link #findDottedName(ELContext, Object, BeanManagerImpl,
ELContextStore, String)}, so
+ * indexing just this (usually tiny or empty) subset avoids a linear scan
over
+ * <b>all</b> managed beans on every EL root-property access.
+ */
+ private volatile NavigableMap<String, Set<Bean<?>>> dottedBeanNameIndex;
public WebBeansELResolver()
{
webBeansContext = WebBeansContext.getInstance();
- dotNamedBeansNegativeCache = new CopyOnWriteArraySet<>();
}
/**
@@ -160,20 +180,17 @@
final ELContextStore elContextStore, final
String beanName)
{
final String fqBeanName = base == null ? beanName : base + "." +
beanName;
- if (dotNamedBeansNegativeCache.contains(fqBeanName))
+ if (dotNamedBeansNegativeCache.containsKey(fqBeanName))
{
return null;
}
- final Set<Bean<?>> anyBeanName = beanManager.getBeans().stream()
- .filter(b -> b.getName() != null)
- .filter(b -> b.getName().equals(fqBeanName) ||
b.getName().startsWith(fqBeanName + "."))
- .collect(Collectors.toSet());
+ final Set<Bean<?>> anyBeanName = getDottedBeanCandidates(beanManager,
fqBeanName);
// no exact and no startsWith match
if (anyBeanName.isEmpty())
{
- dotNamedBeansNegativeCache.add(fqBeanName);
+ dotNamedBeansNegativeCache.put(fqBeanName, Boolean.TRUE);
return null;
}
@@ -188,6 +205,77 @@
return new WrappedValueExpressionNode(fqBeanName);
}
+ /**
+ * Returns all beans whose name is either exactly {@code fqBeanName} or
starts with
+ * {@code fqBeanName + "."}, using a sorted index over dot-named beans
only, instead of
+ * scanning every managed bean.
+ */
+ private Set<Bean<?>> getDottedBeanCandidates(final BeanManagerImpl
beanManager, final String fqBeanName)
+ {
+ NavigableMap<String, Set<Bean<?>>> index =
getDottedBeanNameIndex(beanManager);
+ if (index.isEmpty())
+ {
+ return Collections.emptySet();
+ }
+
+ String prefix = fqBeanName + '.';
+ Set<Bean<?>> result = null;
+
+ Set<Bean<?>> exact = index.get(fqBeanName);
+ if (exact != null)
+ {
+ result = new HashSet<>(exact);
+ }
+
+ for (Map.Entry<String, Set<Bean<?>>> entry : index.tailMap(prefix,
true).entrySet())
+ {
+ if (!entry.getKey().startsWith(prefix))
+ {
+ break;
+ }
+ if (result == null)
+ {
+ result = new HashSet<>();
+ }
+ result.addAll(entry.getValue());
+ }
+
+ return result == null ? Collections.emptySet() : result;
+ }
+
+ /**
+ * Lazily builds (once) an index of all beans with a dot in their name,
sorted by name so that
+ * prefix-matches can be found via a small {@link
NavigableMap#tailMap(Object, boolean)} range
+ * instead of iterating over every bean in the application. This mirrors
the permanent-cache
+ * assumption already used by {@link #dotNamedBeansNegativeCache}: once
the container is up and
+ * serving EL expressions, the deployed bean set is stable.
+ */
+ private NavigableMap<String, Set<Bean<?>>> getDottedBeanNameIndex(final
BeanManagerImpl beanManager)
+ {
+ NavigableMap<String, Set<Bean<?>>> index = dottedBeanNameIndex;
+ if (index == null)
+ {
+ synchronized (this)
+ {
+ index = dottedBeanNameIndex;
+ if (index == null)
+ {
+ index = new TreeMap<>();
+ for (Bean<?> bean : beanManager.getBeans())
+ {
+ final String name = bean.getName();
+ if (name != null && name.indexOf('.') >= 0)
+ {
+ index.computeIfAbsent(name, k -> new
HashSet<>()).add(bean);
+ }
+ }
+ dottedBeanNameIndex = index;
+ }
+ }
+ }
+ return index;
+ }
+
protected Object getNormalScopedContextualInstance(BeanManagerImpl
manager, ELContextStore store, ELContext context,
Bean<?> bean, String
beanName)
{
> Improvide WebBeansELResolver#findDottedName
> -------------------------------------------
>
> Key: OWB-1464
> URL: https://issues.apache.org/jira/browse/OWB-1464
> Project: OpenWebBeans
> Issue Type: Improvement
> Reporter: Thomas Andraschko
> Priority: Major
> Fix For: 4.1.1
>
>
> Old:
> findDottedName()-only: 20000 beans, 20000 distinct lookups => 16283 ms
> (814150.0 ns/lookup)
> New:
> findDottedName()-only: 20000 beans, 20000 distinct lookups => 19 ms (950.0
> ns/lookup)
--
This message was sent by Atlassian Jira
(v8.20.10#820010)