You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@druid.apache.org by fj...@apache.org on 2019/03/11 18:33:10 UTC

[incubator-druid] branch master updated: Cache selectors in QueryableIndexColumnSelectorFactory. (#7216)

This is an automated email from the ASF dual-hosted git repository.

fjy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-druid.git


The following commit(s) were added to refs/heads/master by this push:
     new 4290e5a  Cache selectors in QueryableIndexColumnSelectorFactory. (#7216)
4290e5a is described below

commit 4290e5ae7a9be0cfff68bddae0501602c5ed1c73
Author: Gian Merlino <gi...@gmail.com>
AuthorDate: Mon Mar 11 11:33:02 2019 -0700

    Cache selectors in QueryableIndexColumnSelectorFactory. (#7216)
    
    For selectors with internal caches (like SingleScanTimeDimensionSelector,
    SingleLongInputCachingExpressionColumnValueSelector, etc) we can get a perf
    boost and memory usage decrease by sharing selectors.
---
 .../QueryableIndexColumnSelectorFactory.java       | 93 +++++++++++++---------
 1 file changed, 57 insertions(+), 36 deletions(-)

diff --git a/processing/src/main/java/org/apache/druid/segment/QueryableIndexColumnSelectorFactory.java b/processing/src/main/java/org/apache/druid/segment/QueryableIndexColumnSelectorFactory.java
index c0b5ee5..ec5ee1d 100644
--- a/processing/src/main/java/org/apache/druid/segment/QueryableIndexColumnSelectorFactory.java
+++ b/processing/src/main/java/org/apache/druid/segment/QueryableIndexColumnSelectorFactory.java
@@ -30,6 +30,7 @@ import org.apache.druid.segment.column.ValueType;
 import org.apache.druid.segment.data.ReadableOffset;
 
 import javax.annotation.Nullable;
+import java.util.HashMap;
 import java.util.Map;
 
 /**
@@ -45,8 +46,14 @@ class QueryableIndexColumnSelectorFactory implements ColumnSelectorFactory
   private final Closer closer;
   protected final ReadableOffset offset;
 
+  // Share Column objects, since they cache decompressed buffers internally, and we can avoid recomputation if the
+  // same column is used by more than one part of a query.
   private final Map<String, BaseColumn> columnCache;
 
+  // Share selectors too, for the same reason that we cache columns (they may cache things internally).
+  private final Map<DimensionSpec, DimensionSelector> dimensionSelectorCache;
+  private final Map<String, ColumnValueSelector> valueSelectorCache;
+
   QueryableIndexColumnSelectorFactory(
       QueryableIndex index,
       VirtualColumns virtualColumns,
@@ -62,16 +69,23 @@ class QueryableIndexColumnSelectorFactory implements ColumnSelectorFactory
     this.closer = closer;
     this.offset = offset;
     this.columnCache = columnCache;
+    this.dimensionSelectorCache = new HashMap<>();
+    this.valueSelectorCache = new HashMap<>();
   }
 
   @Override
   public DimensionSelector makeDimensionSelector(DimensionSpec dimensionSpec)
   {
-    if (virtualColumns.exists(dimensionSpec.getDimension())) {
-      return virtualColumns.makeDimensionSelector(dimensionSpec, this);
-    }
-
-    return dimensionSpec.decorate(makeDimensionSelectorUndecorated(dimensionSpec));
+    return dimensionSelectorCache.computeIfAbsent(
+        dimensionSpec,
+        spec -> {
+          if (virtualColumns.exists(spec.getDimension())) {
+            return virtualColumns.makeDimensionSelector(spec, this);
+          }
+
+          return spec.decorate(makeDimensionSelectorUndecorated(spec));
+        }
+    );
   }
 
   private DimensionSelector makeDimensionSelectorUndecorated(DimensionSpec dimensionSpec)
@@ -93,20 +107,10 @@ class QueryableIndexColumnSelectorFactory implements ColumnSelectorFactory
       return type.makeNumericWrappingDimensionSelector(makeColumnValueSelector(dimension), extractionFn);
     }
 
-    BaseColumn column = columnCache.computeIfAbsent(dimension, d -> {
-      BaseColumn col = columnHolder.getColumn();
-      if (col instanceof DictionaryEncodedColumn) {
-        return closer.register(col);
-      } else {
-        // Return null from the lambda in computeIfAbsent() results in no recorded value in the columnCache and
-        // the column variable is set to null.
-        return null;
-      }
-    });
-
-    if (column instanceof DictionaryEncodedColumn) {
-      //noinspection unchecked
-      return ((DictionaryEncodedColumn<String>) column).makeDimensionSelector(offset, extractionFn);
+    final DictionaryEncodedColumn column = getCachedColumn(dimension, DictionaryEncodedColumn.class);
+
+    if (column != null) {
+      return column.makeDimensionSelector(offset, extractionFn);
     } else {
       return DimensionSelector.constant(null, extractionFn);
     }
@@ -115,24 +119,41 @@ class QueryableIndexColumnSelectorFactory implements ColumnSelectorFactory
   @Override
   public ColumnValueSelector<?> makeColumnValueSelector(String columnName)
   {
-    if (virtualColumns.exists(columnName)) {
-      return virtualColumns.makeColumnValueSelector(columnName, this);
-    }
-
-    BaseColumn column = columnCache.computeIfAbsent(columnName, name -> {
-      ColumnHolder holder = index.getColumnHolder(name);
-      if (holder != null) {
-        return closer.register(holder.getColumn());
-      } else {
-        return null;
-      }
-    });
+    return valueSelectorCache.computeIfAbsent(
+        columnName,
+        name -> {
+          if (virtualColumns.exists(columnName)) {
+            return virtualColumns.makeColumnValueSelector(columnName, this);
+          }
+
+          BaseColumn column = getCachedColumn(columnName, BaseColumn.class);
+
+          if (column != null) {
+            return column.makeColumnValueSelector(offset);
+          } else {
+            return NilColumnValueSelector.instance();
+          }
+        }
+    );
+  }
 
-    if (column != null) {
-      return column.makeColumnValueSelector(offset);
-    } else {
-      return NilColumnValueSelector.instance();
-    }
+  @Nullable
+  @SuppressWarnings("unchecked")
+  private <T extends BaseColumn> T getCachedColumn(final String columnName, final Class<T> clazz)
+  {
+    return (T) columnCache.computeIfAbsent(
+        columnName,
+        name -> {
+          ColumnHolder holder = index.getColumnHolder(name);
+          if (holder != null && clazz.isAssignableFrom(holder.getColumn().getClass())) {
+            return closer.register(holder.getColumn());
+          } else {
+            // Return null from the lambda in computeIfAbsent() results in no recorded value in the columnCache and
+            // the column variable is set to null.
+            return null;
+          }
+        }
+    );
   }
 
   @Override


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@druid.apache.org
For additional commands, e-mail: commits-help@druid.apache.org