Skip to content

Commit

Permalink
perf: Remove Unnecessary Recursion from Select/Update (#5924)
Browse files Browse the repository at this point in the history
This reorganizes how select/update builds the resulting formula columns, column source maps, and work to be processed on the PUG. Instead of using recursion, it now builds things up in a dynamic-programming style without redoing work. As a result, select/update operations with thousands of columns now use significantly less memory and cpu to perform the same work.

A simple 50k select-column update operation initializes in 2.5m (source starts empty and this is on an m2 mac) and has cycle times of ~650ms.
  • Loading branch information
nbauernfeind authored Aug 15, 2024
1 parent e919d52 commit 663fae6
Show file tree
Hide file tree
Showing 27 changed files with 1,146 additions and 1,219 deletions.
43 changes: 41 additions & 2 deletions Util/src/main/java/io/deephaven/util/SimpleTypeMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,56 @@

public final class SimpleTypeMap<V> {

public static <V> SimpleTypeMap<V> create(V forBoolean, V forChar, V forByte, V forShort, V forInt, V forLong,
V forFloat, V forDouble, V forObject) {
/**
* Create a mapping from type {@link Class classes} to a value.
*
* @param forBoolean The mapping for {@code boolean} types (<em>note</em> {@link Boolean} maps to {@code forObject})
* @param forChar The mapping for {@code char} and {@link Character} types
* @param forByte The mapping for {@code byte} and {@link Byte} types
* @param forShort The mapping for {@code short} and {@link Short} types
* @param forInt The mapping for {@code int} and {@link Integer} types
* @param forLong The mapping for {@code long} and {@link Long} types
* @param forFloat The mapping for {@code float} and {@link Float} types
* @param forDouble The mapping for {@code double} and {@link Double} types
* @param forObject The mapping for all other types
* @return A SimpleTypeMap to the provided values
*/
public static <V> SimpleTypeMap<V> create(
V forBoolean,
V forChar,
V forByte,
V forShort,
V forInt,
V forLong,
V forFloat,
V forDouble,
V forObject) {
final HashMap<Class<?>, V> map = new HashMap<>();

map.put(boolean.class, forBoolean);
// Note: Booleans are treated as Objects, unlike other boxed primitives

map.put(char.class, forChar);
map.put(Character.class, forChar);

map.put(byte.class, forByte);
map.put(Byte.class, forByte);

map.put(short.class, forShort);
map.put(Short.class, forShort);

map.put(int.class, forInt);
map.put(Integer.class, forInt);

map.put(long.class, forLong);
map.put(Long.class, forLong);

map.put(float.class, forFloat);
map.put(Float.class, forFloat);

map.put(double.class, forDouble);
map.put(Double.class, forDouble);

return new SimpleTypeMap<>(map, forObject);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
package io.deephaven.engine.table.impl;

import io.deephaven.UncheckedDeephavenException;
import io.deephaven.api.util.NameValidator;
import io.deephaven.engine.context.ExecutionContext;
import io.deephaven.engine.context.QueryCompiler;
import io.deephaven.engine.context.QueryCompilerRequest;
import io.deephaven.engine.context.QueryScope;
import io.deephaven.engine.table.impl.perf.QueryPerformanceRecorder;
import io.deephaven.engine.table.impl.select.codegen.FormulaAnalyzer;
import io.deephaven.util.MultiException;
import io.deephaven.util.SafeCloseable;
import io.deephaven.util.CompletionStageFuture;
Expand All @@ -18,68 +17,60 @@
import org.jetbrains.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public interface QueryCompilerRequestProcessor {
public abstract class QueryCompilerRequestProcessor {

/**
* @return An immediate QueryCompilerRequestProcessor
*/
static QueryCompilerRequestProcessor.ImmediateProcessor immediate() {
public static QueryCompilerRequestProcessor.ImmediateProcessor immediate() {
return new ImmediateProcessor();
}

/**
* @return A batch QueryCompilerRequestProcessor
*/
static QueryCompilerRequestProcessor.BatchProcessor batch() {
public static QueryCompilerRequestProcessor.BatchProcessor batch() {
return new BatchProcessor();
}

/**
* @return a CachingSupplier that supplies a snapshot of the current query scope variables
* @return a CachingSupplier that supplies a snapshot of current query scope variables and query library imports
*/
@VisibleForTesting
static CachingSupplier<Map<String, Object>> newQueryScopeVariableSupplier() {
final QueryScope queryScope = ExecutionContext.getContext().getQueryScope();
return new CachingSupplier<>(() -> Collections.unmodifiableMap(
queryScope.toMap((name, value) -> NameValidator.isValidQueryParameterName(name))));
public static CachingSupplier<FormulaAnalyzer.Imports> newFormulaImportsSupplier() {
return new CachingSupplier<>(FormulaAnalyzer.Imports::new);
}

private final CachingSupplier<FormulaAnalyzer.Imports> formulaImportsSupplier = newFormulaImportsSupplier();

/**
* @return a lazily cached snapshot of the current query scope variables
* @return a lazily cached snapshot of current query scope variables and query library imports
*/
Map<String, Object> getQueryScopeVariables();
public final FormulaAnalyzer.Imports getFormulaImports() {
return formulaImportsSupplier.get();
}

/**
* Submit a request for compilation. The QueryCompilerRequestProcessor is not required to immediately compile this
* request.
*
* @param request the request to compile
*/
CompletionStageFuture<Class<?>> submit(@NotNull QueryCompilerRequest request);
public abstract CompletionStageFuture<Class<?>> submit(@NotNull QueryCompilerRequest request);

/**
* A QueryCompilerRequestProcessor that immediately compiles requests.
*/
class ImmediateProcessor implements QueryCompilerRequestProcessor {

private final CachingSupplier<Map<String, Object>> queryScopeVariableSupplier = newQueryScopeVariableSupplier();

public static class ImmediateProcessor extends QueryCompilerRequestProcessor {
private ImmediateProcessor() {
// force use of static factory method
}

@Override
public Map<String, Object> getQueryScopeVariables() {
return queryScopeVariableSupplier.get();
}

@Override
public CompletionStageFuture<Class<?>> submit(@NotNull final QueryCompilerRequest request) {
final String desc = "Compile: " + request.description();
Expand Down Expand Up @@ -108,20 +99,14 @@ public CompletionStageFuture<Class<?>> submit(@NotNull final QueryCompilerReques
* <p>
* The compile method must be called to actually compile the requests.
*/
class BatchProcessor implements QueryCompilerRequestProcessor {
public static class BatchProcessor extends QueryCompilerRequestProcessor {
private final List<QueryCompilerRequest> requests = new ArrayList<>();
private final List<CompletionStageFuture.Resolver<Class<?>>> resolvers = new ArrayList<>();
private final CachingSupplier<Map<String, Object>> queryScopeVariableSupplier = newQueryScopeVariableSupplier();

private BatchProcessor() {
// force use of static factory method
}

@Override
public Map<String, Object> getQueryScopeVariables() {
return queryScopeVariableSupplier.get();
}

@Override
public CompletionStageFuture<Class<?>> submit(@NotNull final QueryCompilerRequest request) {
final CompletionStageFuture.Resolver<Class<?>> resolver = CompletionStageFuture.make();
Expand Down
Loading

0 comments on commit 663fae6

Please sign in to comment.