-
Notifications
You must be signed in to change notification settings - Fork 980
CG Scalar Replacement
Applying a bit of "software archeology", it seems that Drill started by providing functions for most tasks using the form still used for UDFs:
@FunctionTemplate(name = FunctionGenerationHelper.COMPARE_TO_NULLS_HIGH,
scope = FunctionTemplate.FunctionScope.SIMPLE,
nulls = NullHandling.INTERNAL)
public static class GCompareNullableVarCharVsVarCharNullHigh implements DrillSimpleFunc {
@Param NullableVarCharHolder left;
@Param VarCharHolder right;
@Output IntHolder out;
public void setup() {}
public void eval() {
outside:
{
if ( left.isSet == 0 ) {
out.value = 1;
break outside;
}
out.value = org.apache.drill.exec.expr.fn.impl.ByteFunctionHelpers.compare(
left.buffer, left.start, left.end, right.buffer, right.start, right.end );
} // outside
}
}
Because there are a very large number of such functions, they are generated (using Freemarker):
<#-- Comparison function for sorting and grouping relational operators
(not for comparison expression operators (=, <, etc.)). -->
@FunctionTemplate(name = FunctionGenerationHelper.COMPARE_TO_NULLS_HIGH,
scope = FunctionTemplate.FunctionScope.SIMPLE,
nulls = NullHandling.INTERNAL)
public static class GCompare${leftType}Vs${rightType}NullHigh implements DrillSimpleFunc {
@Param ${leftType}Holder left;
@Param ${rightType}Holder right;
@Output IntHolder out;
public void setup() {}
public void eval() {
<@compareBlock mode=typeGroup.mode leftType=leftType rightType=rightType
output="out.value" nullCompare=true nullComparesHigh=true />
}
}
At some point, Drill moved to generating code for these functions instead of calling the (statically generated and compiled) functions. The code generator seems to use the code from the above functions and combines it with generated code to get the source code for the operator-specific bits of the logic:
public class SingleBatchSorterGen7 {
NullableVarCharVector vv0;
NullableVarCharVector vv4;
public int doEval(char leftIndex, char rightIndex)
throws SchemaChangeException
{
{
NullableVarCharHolder out3 = new NullableVarCharHolder();
{
out3 .isSet = vv0 .getAccessor().isSet((leftIndex));
if (out3 .isSet == 1) {
out3 .buffer = vv0 .getBuffer();
long startEnd = vv0 .getAccessor().getStartEnd((leftIndex));
out3 .start = ((int) startEnd);
out3 .end = ((int)(startEnd >> 32));
}
}
...
//---- start of eval portion of compare_to_nulls_high function. ----//
IntHolder out8 = new IntHolder();
{
final IntHolder out = new IntHolder();
NullableVarCharHolder left = out3;
NullableVarCharHolder right = out7;
GCompareVarCharVsVarChar$GCompareNullableVarCharVsNullableVarCharNullHigh_eval: {
outside:
{
if (left.isSet == 0) {
if (right.isSet == 0) {
out.value = 0;
break outside;
} else
{
out.value = 1;
break outside;
}
} else
if (right.isSet == 0) {
out.value = -1;
break outside;
}
out.value = org.apache.drill.exec.expr.fn.impl.ByteFunctionHelpers.compare(
left.buffer,
left.start,
left.end,
right.buffer,
right.start,
right.end
);
}
}
out8 = out;
}
//---- end of eval portion of compare_to_nulls_high function. ----//
if (out8 .value!= 0) {
return out8 .value;
}
return 0;
}
...
}
Notice that that source code makes use of temporary objects:
NullableVarCharHolder out3 = new NullableVarCharHolder();
Scalar replacement is a technique to replace such objects with local scalar variables. Drill uses the ASM library to do the work rather than relying on the JVM to do it. From the decompilation of the final generated code:
NullableVarCharHolder out3;
int i = 0; int j = 0; int k = 0; DrillBuf localDrillBuf1 = null;
...
if (i == 0) {
if (m == 0) {
i3 = 0;
}
else
{
i3 = 1;
}
Here we can see that the NullableVarCharHolder
has been removed; replaced by a series of local variables.