wuchong commented on code in PR #22734: URL: https://github.com/apache/flink/pull/22734#discussion_r1241200132
########## flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/fusion/OpFusionCodegenSpec.java: ########## @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.planner.plan.fusion; + +import org.apache.flink.annotation.Internal; +import org.apache.flink.table.data.RowData; +import org.apache.flink.table.data.binary.BinaryRowData; +import org.apache.flink.table.planner.codegen.CodeGeneratorContext; +import org.apache.flink.table.planner.codegen.ExprCodeGenerator; +import org.apache.flink.table.planner.codegen.GeneratedExpression; + +import java.util.List; +import java.util.Set; + +/** An interface for those physical operators that support operator fusion codegen. */ +@Internal +public interface OpFusionCodegenSpec { + + /** + * Initializes the operator spec. Sets access to the context. This method must be called before + * doProduce and doConsume related methods. + */ + void setup(OpFusionContext opFusionContext); + + /** Prefix used in the current operator's variable names. */ + String variablePrefix(); Review Comment: Is it necessary to provide this interface? Is it enough to use the concrete class name of `OpFusionCodegenSpec` as the variable prefix? IIUC, the variable prefix is just used for better readability. ########## flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/fusion/OpFusionCodegenSpec.java: ########## @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.planner.plan.fusion; + +import org.apache.flink.annotation.Internal; +import org.apache.flink.table.data.RowData; +import org.apache.flink.table.data.binary.BinaryRowData; +import org.apache.flink.table.planner.codegen.CodeGeneratorContext; +import org.apache.flink.table.planner.codegen.ExprCodeGenerator; +import org.apache.flink.table.planner.codegen.GeneratedExpression; + +import java.util.List; +import java.util.Set; + +/** An interface for those physical operators that support operator fusion codegen. */ +@Internal +public interface OpFusionCodegenSpec { + + /** + * Initializes the operator spec. Sets access to the context. This method must be called before + * doProduce and doConsume related methods. + */ + void setup(OpFusionContext opFusionContext); + + /** Prefix used in the current operator's variable names. */ + String variablePrefix(); + + /** + * The subset of column index those should be evaluated before this operator. + * + * <p>We will use this to insert some code to access those columns that are actually used by + * current operator before calling doProcessConsume(). + */ + Set<Integer> usedInputVars(int inputId); Review Comment: `usedInputVars` -> `usedInputColumns`? It's not obvious to reflect the `Vars` to `Columns`. I thought this returns the used variables of input terms... ########## flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/fusion/OpFusionCodegenSpecGenerator.java: ########## @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.planner.plan.fusion; + +import org.apache.flink.annotation.Internal; +import org.apache.flink.table.planner.codegen.CodeGeneratorContext; +import org.apache.flink.table.planner.codegen.GeneratedExpression; +import org.apache.flink.table.types.logical.RowType; + +import java.util.List; + +/** + * {@link OpFusionCodegenSpecGenerator} is used to operator fusion codegen that generate the fusion + * code, it has multiple inputs and outputs, then form a DAG. Every OpFusionCodegenSpecGenerator + * holds an {@link OpFusionCodegenSpec} that used to generate the operator process row code. In + * addition, it also provides some meta information that codegen needed. + */ +@Internal +public abstract class OpFusionCodegenSpecGenerator { + + private final RowType outputType; + protected final OpFusionCodegenSpec opFusionCodegenSpec; + + private double managedMemoryFraction = 0; + + public OpFusionCodegenSpecGenerator( + RowType outputType, OpFusionCodegenSpec opFusionCodegenSpec) { + this.outputType = outputType; + this.opFusionCodegenSpec = opFusionCodegenSpec; + } + + /** + * Initializes the operator spec generator needed information. This method must be called before + * produce and consume related method. + */ + public void setup(Context context) { + this.managedMemoryFraction = context.getManagedMemoryFraction(); + this.opFusionCodegenSpec.setup(new OpFusionContextImpl(this)); + } + + public RowType getOutputType() { + return outputType; + } + + public OpFusionCodegenSpec getOpFusionCodegenSpec() { + return opFusionCodegenSpec; + } + + public abstract long getManagedMemory(); + + public abstract List<OpFusionCodegenSpecGenerator> getInputs(); + + public abstract void addOutput(int inputIdOfOutput, OpFusionCodegenSpecGenerator output); Review Comment: Add more description about this method and parameters? ########## flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/nodes/exec/batch/BatchExecHashJoin.java: ########## @@ -293,4 +299,61 @@ private long getLargeManagedMemory(FlinkJoinType joinType, ExecNodeConfig config // large one return Math.max(hashJoinManagedMemory, sortMergeJoinManagedMemory); } + + @Override + public boolean supportFusionCodegen() { + RowType leftType = (RowType) getInputEdges().get(0).getOutputType(); + LogicalType[] keyFieldTypes = + IntStream.of(joinSpec.getLeftKeys()) + .mapToObj(leftType::getTypeAt) + .toArray(LogicalType[]::new); + RowType keyType = RowType.of(keyFieldTypes); + FlinkJoinType joinType = joinSpec.getJoinType(); + HashJoinType hashJoinType = + HashJoinType.of( + leftIsBuild, + joinType.isLeftOuter(), + joinType.isRightOuter(), + joinType == FlinkJoinType.SEMI, + joinType == FlinkJoinType.ANTI); + // TODO decimal and multiKeys support and all HashJoinType support. + return LongHashJoinGenerator.support(hashJoinType, keyType, joinSpec.getFilterNulls()); Review Comment: Is there any reason that we only support LongHashJoin for fusion codegen in the first version? ########## flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/GeneratedExpression.scala: ########## @@ -101,6 +108,37 @@ case class GeneratedExpression( this } } + + /** Deep copy code of mutable field generated expression. */ + def deepCopyMutableExpr(ctx: CodeGeneratorContext): String = { + // only copy when type is mutable + if (TypeCheckUtils.isMutable(resultType)) { + deepCopy(ctx).getCode + } else { + "" + } + } + + def getCode(): String = { + codeUsed match { + // if the code has been wrapped into generated code block, doesn't evaluate it again + case true => NO_CODE + case false => + codeUsed = true + code + } + } + + /** + * This is used only for OFCG, we need to copy the expr to avoid the code is used when prepare + * rowVar. + */ + def copyExpr(): GeneratedExpression = { + codeUsed match { + case true => GeneratedExpression(resultTerm, nullTerm, NO_CODE, resultType, literalValue) + case false => GeneratedExpression(resultTerm, nullTerm, code, resultType, literalValue) + } + } Review Comment: The `codeUsed` related methods and code look very hacky to me. Because you have to take `codeUsed` into account for everywhere you want to get code of the expression, even for non-OFCG. From my perspective of view, this part code is introduced just for OFCG to not generate code twice. Can we move this logic out of the definition of `GeneratedExpression`? I think you can copy a new list of `GeneratedExpression`s with empty code if you want to materialize all the code first. ########## flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/fusion/OpFusionContext.java: ########## @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.planner.plan.fusion; + +import org.apache.flink.table.planner.codegen.GeneratedExpression; +import org.apache.flink.table.types.logical.RowType; + +import java.util.List; + +/** + * A OpFusionContext contains information about the context in which {@link OpFusionCodegenSpec} + * needed to do operator fusion codegen. + */ +public interface OpFusionContext { + + /** Return the output type of current {@link OpFusionCodegenSpecGenerator}. */ + RowType getOutputType(); + + /** + * Return the managed memory fraction of this {@link OpFusionCodegenSpecGenerator} needed during + * all fusion operators. + */ + double getManagedMemoryFraction(); + + /** Return the inputs of owner {@link OpFusionCodegenSpecGenerator} which holds this context. */ + List<OpFusionCodegenSpecGenerator> getInputs(); Review Comment: The purpose of `OpFusionContext` is to want to hide the concrete implementation detail of `OpFusionCodegenSpecGenerator`. However, this `getInputs` expose `OpFusionCodegenSpecGenerator` which makes the `OpFusionContext` meaningless. If we need to expose `OpFusionCodegenSpecGenerator` to `OpFusionCodegenSpec` anyway, why not just pass the `OpFusionCodegenSpecGenerator` instead of `OpFusionContext`? I checked all the invoking of `getInputs` which are only used to call `processProduce` and `endInputProduce`. Could we just expose them in `OpFusionContext` just like `processConsume` and additionally expose `List<OpFusionContext> getInputFusionContexts()`? In this way, the invoking can just simply call `fusionCtx.getInputFusionContexts.head.processProduce(codegenCtx)`. ########## flink-table/flink-table-runtime/src/test/java/org/apache/flink/table/runtime/operators/multipleinput/input/InputSelectionHandlerTest.java: ########## @@ -41,7 +42,11 @@ public void testWithSamePriority() { new InputSpec(3, 0, createTwoInputOperatorWrapper("input3"), 1), new InputSpec(4, 0, createTwoInputOperatorWrapper("input4"), 2), new InputSpec(5, 0, createOneInputOperatorWrapper("input5"), 1)); - InputSelectionHandler handler = new InputSelectionHandler(inputSpecs); + InputSelectionHandler handler = + new InputSelectionHandler( + inputSpecs.stream() + .map(InputSpec::getMultipleInputSpec) + .collect(Collectors.toList())); Review Comment: We can still keep the `List<InputSpec>` constructor of `InputSelectionHandler` to avoid verbose type conversion here and there. ########## flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/fusion/spec/InputAdapterFusionCodegenSpec.scala: ########## @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.flink.table.planner.plan.fusion.spec + +import org.apache.flink.streaming.api.operators.AbstractInput +import org.apache.flink.table.planner.codegen.{CodeGeneratorContext, GeneratedExpression} +import org.apache.flink.table.planner.codegen.CodeGenUtils.{boxedTypeTermForType, className, DEFAULT_INPUT_TERM} +import org.apache.flink.table.planner.codegen.OperatorCodeGenerator.{ELEMENT, STREAM_RECORD} +import org.apache.flink.table.planner.plan.fusion.OpFusionCodegenSpecBase + +import java.util + +/** The operator fusion codegen spec for input operator. */ +class InputAdapterFusionCodegenSpec(operatorCtx: CodeGeneratorContext, inputId: Int) + extends OpFusionCodegenSpecBase(operatorCtx) { + + override def variablePrefix: String = "input" + + override protected def doProcessProduce(fusionCtx: CodeGeneratorContext): Unit = { + val inputTypeTerm = boxedTypeTermForType(fusionContext.getOutputType) Review Comment: It's very confusing there are `fusionCtx` and `fusionContext` with different type and purpose in one method. ########## flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/fusion/OpFusionCodegenSpec.java: ########## @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.planner.plan.fusion; + +import org.apache.flink.annotation.Internal; +import org.apache.flink.table.data.RowData; +import org.apache.flink.table.data.binary.BinaryRowData; +import org.apache.flink.table.planner.codegen.CodeGeneratorContext; +import org.apache.flink.table.planner.codegen.ExprCodeGenerator; +import org.apache.flink.table.planner.codegen.GeneratedExpression; + +import java.util.List; +import java.util.Set; + +/** An interface for those physical operators that support operator fusion codegen. */ +@Internal +public interface OpFusionCodegenSpec { + + /** + * Initializes the operator spec. Sets access to the context. This method must be called before + * doProduce and doConsume related methods. + */ + void setup(OpFusionContext opFusionContext); + + /** Prefix used in the current operator's variable names. */ + String variablePrefix(); + + /** + * The subset of column index those should be evaluated before this operator. + * + * <p>We will use this to insert some code to access those columns that are actually used by + * current operator before calling doProcessConsume(). + */ + Set<Integer> usedInputVars(int inputId); + + /** + * Specific inputId of current operator needed {@link RowData} type, this is used to notify the + * upstream operator wrap the proper {@link RowData} we needed before call doProcessConsume + * method. For example, HashJoin build side need {@link BinaryRowData}. + */ + Class<? extends RowData> getInputRowDataClass(int inputId); + + /** + * Every operator need one {@link CodeGeneratorContext} to store the context needed during + * operator fusion codegen. + */ + CodeGeneratorContext getOperatorCtx(); Review Comment: `getOperatorCtx()` -> `getCodeGeneratorContext()`? It looks strange that the `getOperatorCtx` returns a `CodeGeneratorContext` not a `OpFusionContext`. ########## flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/operators/multipleinput/MultipleInputSpec.java: ########## @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.table.runtime.operators.multipleinput; + +import org.apache.flink.streaming.api.operators.Input; +import org.apache.flink.streaming.api.operators.MultipleInputStreamOperator; + +import java.io.Serializable; +import java.util.Objects; + +/** Describe the inputId and read order of MultipleInput operator. */ +public class MultipleInputSpec implements Serializable { Review Comment: Should this be put in the same package as `InputSpec`? Besides, from the naming of `MultipleInputSpec` and `InputSepc`, it feels like `MultipleInputSpec` is a subclass of `InputSpec`, but it is not which confuses me. ########## flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/fusion/spec/HashJoinFusionCodegenSpec.scala: ########## @@ -0,0 +1,542 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.flink.table.planner.plan.fusion.spec + +import org.apache.flink.table.data.RowData +import org.apache.flink.table.data.binary.BinaryRowData +import org.apache.flink.table.planner.codegen.{CodeGeneratorContext, GeneratedExpression, GenerateUtils} +import org.apache.flink.table.planner.codegen.CodeGenUtils.{fieldIndices, newName, newNames, primitiveDefaultValue, primitiveTypeTermForType, BINARY_ROW, ROW_DATA} +import org.apache.flink.table.planner.codegen.LongHashJoinGenerator.{genGetLongKey, genProjection} +import org.apache.flink.table.planner.plan.fusion.{OpFusionCodegenSpecBase, OpFusionCodegenSpecGenerator, OpFusionContext} +import org.apache.flink.table.planner.plan.nodes.exec.spec.JoinSpec +import org.apache.flink.table.planner.utils.JavaScalaConversionUtil.{toJava, toScala} +import org.apache.flink.table.runtime.hashtable.LongHybridHashTable +import org.apache.flink.table.runtime.operators.join.{FlinkJoinType, HashJoinType} +import org.apache.flink.table.runtime.typeutils.BinaryRowDataSerializer +import org.apache.flink.table.runtime.util.RowIterator +import org.apache.flink.table.types.logical.{LogicalType, RowType} + +import java.util + +/** Base operator fusion codegen spec for HashJoin. */ +class HashJoinFusionCodegenSpec( + operatorCtx: CodeGeneratorContext, + isBroadcast: Boolean, + leftIsBuild: Boolean, + joinSpec: JoinSpec, + estimatedLeftAvgRowSize: Int, + estimatedRightAvgRowSize: Int, + estimatedLeftRowCount: Long, + estimatedRightRowCount: Long, + compressionEnabled: Boolean, + compressionBlockSize: Int) + extends OpFusionCodegenSpecBase(operatorCtx) { + + private lazy val joinType: FlinkJoinType = joinSpec.getJoinType + private lazy val hashJoinType: HashJoinType = HashJoinType.of( + leftIsBuild, + joinType.isLeftOuter, + joinType.isRightOuter, + joinType == FlinkJoinType.SEMI, + joinType == FlinkJoinType.ANTI) + private lazy val (buildKeys, probeKeys) = if (leftIsBuild) { + (joinSpec.getLeftKeys, joinSpec.getRightKeys) + } else { + (joinSpec.getRightKeys, joinSpec.getLeftKeys) + } + private lazy val (buildRowSize, buildRowCount) = if (leftIsBuild) { + (estimatedLeftAvgRowSize, estimatedLeftRowCount) + } else { + (estimatedRightAvgRowSize, estimatedRightRowCount) + } + private lazy val buildInputId = if (leftIsBuild) { + 1 + } else { + 2 + } + + private lazy val Seq(buildToBinaryRow, probeToBinaryRow) = + newNames("buildToBinaryRow", "probeToBinaryRow") + + private lazy val hashTableTerm: String = newName("hashTable") + + private var buildInput: OpFusionCodegenSpecGenerator = _ + private var probeInput: OpFusionCodegenSpecGenerator = _ + private var buildType: RowType = _ + private var probeType: RowType = _ + private var keyType: RowType = _ + + override def setup(opFusionContext: OpFusionContext): Unit = { + super.setup(opFusionContext) + val inputs = toScala(fusionContext.getInputs) + assert(inputs.size == 2) + if (leftIsBuild) { + buildInput = inputs.head + probeInput = inputs(1) + } else { + buildInput = inputs(1) + probeInput = inputs.head + } + + buildType = buildInput.getOutputType + probeType = probeInput.getOutputType + if (leftIsBuild) { + keyType = RowType.of(joinSpec.getLeftKeys.map(idx => buildType.getTypeAt(idx)): _*) + } else { + keyType = RowType.of(joinSpec.getLeftKeys.map(idx => probeType.getTypeAt(idx)): _*) + } + } + + override def variablePrefix: String = if (isBroadcast) { "bhj" } + else { "shj" } + + override protected def doProcessProduce(fusionCtx: CodeGeneratorContext): Unit = { + // call build side first, then call probe side + buildInput.processProduce(fusionCtx) + probeInput.processProduce(fusionCtx) + } + + override protected def doEndInputProduce(fusionCtx: CodeGeneratorContext): Unit = { + // call build side first, then call probe side + buildInput.endInputProduce(fusionCtx) + probeInput.endInputProduce(fusionCtx) + } + + override def doProcessConsume( + inputId: Int, + inputVars: util.List[GeneratedExpression], + row: GeneratedExpression): String = { + // only probe side will call the consumeProcess method to consume the output record + if (inputId == buildInputId) { + codegenBuild(toScala(inputVars), row) + } else { + codegenProbe(inputVars) + } + } + + private def codegenBuild( + inputVars: Seq[GeneratedExpression], + row: GeneratedExpression): String = { + // initialize hash table related code + if (isBroadcast) { + codegenHashTable(false) + } else { + // TODO Shuffled HashJoin support build side spill to disk Review Comment: Will a follow-up PR support this? ########## flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/GenerateUtils.scala: ########## @@ -449,14 +449,17 @@ object GenerateUtils { * whether the input is nullable * @param deepCopy * whether to copy the accessed field (usually needed when buffered) + * @param fusionCodegen + * if fusion codegen enabled, don't need to hide generated code Review Comment: Why fusion codegen doesn't need to hide generated code? I mean why fusion codegen doesn't follow other's way to reuse the unboxing code and return expr with code erased? -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
