3

虽然我用 SSIS 标记了这个问题,但它不一定是我问题的核心;因此,如果您通常熟悉 .NET 反射和代码性能问题,请继续阅读,因为您可能会有所帮助!

但特别是,相关代码将被执行多次(即针对每行的数据源)这一事实是关键。

我正在更新一个预先存在的自定义数据流控制。

对于行中的每一列,代码必须在Microsoft.SqlServer.Dts.Pipeline.PipelineBuffer实例(行)上执行特定方法,具体取决于(列)SSIS 数据类型。

例如,

public override void ProcessInput(int inputID, PipelineBuffer buffer)
{
    ...
    while (!buffer.EndOfRowset && buffer.NextRow())
    {
         ...
         someStringAtColumn1 = buffer.GetString(1);
         someIntAtColumn2 = buffer.GetInt16(2);
         someBoolAtColumn3 = buffer.GetBoolean(3);  

         //  And so on, for up to ~25 different types....
     }
     ...
}

但是,控件需要是动态的,因此我们在设计时不知道每列在运行时将是哪种类型。

为此,在SSIS Junkie SSIS 上发布的模式:用于填充管道缓冲区列的通用方法已被使用(并且有效)。总之,这使用 aSWITCH (buffer.GetColumnInfo(columnIndex).DataType)来决定调用 ~25 个方法中的哪一个buffer

所以,我的问题:

  1. SWITCH是否对每一行(可能数百万)的每一列(每个表的数量不同,但假设平均 10 列)重复此语句,可能会导致显着的性能影响(就处理时间而言)?

  2. 最好在每个表的基础上(即逐行处理开始之前)为每列后期绑定到正确的方法,然后对每一列执行特定于列的后期绑定方法(在逐行处理期间)?

我正在考虑的方法是这样的:

// Set up per-column late-bound methods, once, prior to processing the rows
  System.Reflection.MethodInfo[] lateBoundMethods;

  //Psuedo code here for brevity...
  foreach column in tableDefinition {
      lateBoundMethods[i++] = getColumnSpecificGetValueMethod(column.DataType);
  }
  //End of psuedo code


private System.Reflection.MethodInfo getColumnSpecificGetValueMethod(DataType dataType)
{
    string methodName = "";

    switch (dataType)
    {
        case DataType.DT_BOOL:
            methodName = "GetBoolean";
            break;
        case DataType.DT_BYTES:
            methodName = "GetBytes";
            break;
        case DataType.DT_CY:
            methodName = "GetDecimal";
            break;

            ...

        case DataType.DT_WSTR:
            methodName = "GetString";
            break;
        default:
            return null; //TODO: Throw an exception?
    }

    System.Reflection.MethodInfo methodInfo = typeof(PipelineBuffer).GetMethod(
        methodName,
        System.Reflection.BindingFlags.ExactBinding |
        System.Reflection.BindingFlags.Instance |
        System.Reflection.BindingFlags.Public);

    return methodInfo;
}

private object getValueFromBuffer(PipelineBuffer buffer, int columnIndex)
{
    if (buffer.IsNull(columnIndex))
    {
        return null;
    }
    return lateBoundMethods[columnIndex].Invoke(buffer, new object[] { columnIndex});
}

然后,在行处理期间,对于每一列我只需要调用

Object columnValue = getValueFromBuffer(buffer, columnIndex);.

所以我猜问题 2 归结为“针对已经绑定的执行 a会比执行大开关/案例更快吗?.InvokeMethodInfo ”。

编辑:我很欣赏反射通常被认为是缓慢的。但是根据上面的大胆问题,我不清楚反射的哪些部分是缓慢的。我很高兴在执行前设置阶段表现不佳,只要调用后期绑定方法比在处理行时选择要调用的方法更快。因此,对于任何说明这会很慢的答案,您能否澄清一下,您认为调用后期绑定方法会很慢,而不是识别要绑定哪个方法的任务。

我意识到没有答案会像运行一些测试那样确定......但我在投入大量精力之前一直在寻找一些(合理的)权威指示,因为将 ETL 部署到我们的 TEST 环境中进行性能测试并非易事.

此外,我愿意接受有关其他更好方法的建议。

感谢您的时间和投入!

4

1 回答 1

0

您似乎对每一行都使用了反射,这将非常缓慢。我建议您使用字典而不是 switch 语句并缓存反射方法(例如,每个方法只反射一次 = ~25 次 + N 字典查找而不是 N 次反射)。这将大大提高性能。

正如您所建议的,最终的答案当然是编写一个快速原型并运行一个分析器。

于 2013-04-15T10:42:30.917 回答