Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/296.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# IL优化尝试导致执行速度变慢_C#_Optimization_Il - Fatal编程技术网

C# IL优化尝试导致执行速度变慢

C# IL优化尝试导致执行速度变慢,c#,optimization,il,C#,Optimization,Il,考虑这更多的是一个学术问题,而不是实际问题 在重新发明轮子(即编写小型ORM/类型映射器)时,我发出了一些IL,将对象的属性转换为添加到SqlCommand的SqlParameters 我的第一次尝试基本上是编写C#代码并将其翻译成IL 1:1,从而产生以下代码(工作完美)。请注意,注释表示运行以下命令后的堆栈: int paramLocIndex = localIndex++; // Get the type handler [typeHandler] il.Emit(OpCodes.Ca

考虑这更多的是一个学术问题,而不是实际问题

在重新发明轮子(即编写小型ORM/类型映射器)时,我发出了一些IL,将对象的属性转换为添加到SqlCommand的SqlParameters

我的第一次尝试基本上是编写C#代码并将其翻译成IL 1:1,从而产生以下代码(工作完美)。请注意,注释表示运行以下命令后的堆栈:

int paramLocIndex = localIndex++;

 // Get the type handler [typeHandler]
il.Emit(OpCodes.Call, ClrTypeHandlers[prop.PropertyType].GetType().GetMethod("GetTypeHandler", BindingFlags.NonPublic | BindingFlags.Static));

// Load the object [typeHandler, object]
il.Emit(OpCodes.Ldloc_0);

// Get the property value [typeHandler, value]
il.Emit(OpCodes.Call, prop.GetMethod);

// Box the value [typeHandler, boxedValue]
il.Emit(OpCodes.Box, prop.PropertyType);

// Let the type handler create the param [param]
il.Emit(OpCodes.Callvirt, typeof(SqlTypeHandler).GetMethod("CreateParamFromValue", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(object) }, null));

// Store the parameter as a variable []
il.DeclareLocal(typeof(SqlParameter));
il.Emit(OpCodes.Stloc, paramLocIndex);

// Load the parameter again [param]
il.Emit(OpCodes.Ldloc, paramLocIndex);

// Load the parameter name [param, paramName]
il.Emit(OpCodes.Ldstr, paramName);

// Set the parameter name []
il.Emit(OpCodes.Call, typeof(SqlParameter).GetMethod("set_ParameterName"));

// Load the command [cmd]
il.Emit(OpCodes.Ldarg_0);

// Load the parameter collection [paramCollection]
il.Emit(OpCodes.Call, typeof(SqlCommand).GetMethod("get_Parameters"));

// Load the parameter [paramCollection, param]
il.Emit(OpCodes.Ldloc, paramLocIndex);

// Add the parameter to the collection [param]
il.Emit(OpCodes.Call, typeof(SqlParameterCollection).GetMethod("Add", new[] { typeof(SqlParameter) }));

// Get rid of the added parameter, as returned by SqlParameterCollection.Add []
il.Emit(OpCodes.Pop);
虽然上面的工作,我想尝试优化它。这产生了以下代码,这些代码也可以完美地工作:

// Load the command [cmd]
il.Emit(OpCodes.Ldarg_0);

// Load the parameter collection [paramCollection]
il.Emit(OpCodes.Call, typeof(SqlCommand).GetMethod("get_Parameters"));

// Get the type handler [paramCollection, typeHandler]
il.Emit(OpCodes.Call, ClrTypeHandlers[prop.PropertyType].GetType().GetMethod("GetTypeHandler", BindingFlags.NonPublic | BindingFlags.Static));

// Load the object [paramCollection, typeHandler, object]
il.Emit(OpCodes.Ldloc_0);

// Get the property value [paramCollection, typeHandler, value]
il.Emit(OpCodes.Call, prop.GetMethod);

// Box the value [paramCollection, typeHandler, boxedValue]
il.Emit(OpCodes.Box, prop.PropertyType);

// Let the type handler create the param [paramCollection, param]
il.Emit(OpCodes.Callvirt, typeof(SqlTypeHandler).GetMethod("CreateParamFromValue", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(object) }, null));

// Add the parameter to the collection [param]
il.Emit(OpCodes.Call, typeof(SqlParameterCollection).GetMethod("Add", new[] { typeof(SqlParameter) }));

// Load the parameter name [param, paramName]
il.Emit(OpCodes.Ldstr, paramName);

// Set the parameter name []
il.Emit(OpCodes.Call, typeof(SqlParameter).GetMethod("set_ParameterName"));
虽然优化后的尝试会导致更少的指令、避免变量等,但运行速度仍然较慢。第一次尝试运行时间为21500次,而“优化”版本运行时间为75000次


这两个都很快,但我很好奇为什么第一个跑得那么快。我知道从IL到机器代码还有第二级编译——答案可能在于IL编译器执行的优化。问题是,如果我想进一步探讨这个问题,我有什么选择?第二次IL尝试运行较慢,有什么明显的原因吗?

我认为这与IL编译优化无关,但与框架处理添加到SqlParameterCollection的更无聊的方式有关

在第一种(较长)方法中,您可以:

  • 创建SqlParameter
  • 指定其属性名
  • 将其添加到集合中
在优化版本中,您可以:

  • 创建SqlParameter
  • 将其添加到集合中
  • 指定其属性名
注意到区别了吗?最后两个步骤进行了转换,这看起来相当学术,但如果启动reflector并查看SqlParameterCollection源代码,您会偶然发现以下代码:

private void Validate(int index, object value)
{
    /* snip */
    if (((SqlParameter) value).ParameterName.Length == 0)
    {
        string str;
        index = 1;
        do
        {
            str = "Parameter" + index.ToString(CultureInfo.CurrentCulture);
            index++;
        }
        while (-1 != this.IndexOf(str));
        ((SqlParameter) value).ParameterName = str;
    }
}
当您将其添加到集合中而不使用参数名时,会为其生成一个参数,此过程涉及分配、循环和列表查找,这应该很容易解释您所注意到的额外运行时间


如果您试图避免创建局部变量,SqlTypeHandler似乎是您控制的类?因此,您可以将参数名称作为参数添加到CreateParamFromValue方法中,并在其中分配它,这样当它添加到集合中时,它已经有了一个名称。

属于@Shiva Ah,但不知道该名称。如果一个mod可以移动它,那就太好了:)这听起来确实像是发生了什么。让我今晚试一试,然后回来汇报,谢谢!:)是的,这就成功了,减少到了大约2万个滴答声。仍然希望有更大的差异,但这绝对是原因。谢谢