假设:
动机:
示例:
public static List<Vector2> Wavefronts(Vertex[] vertices, float s) {
var result = new List<Vector2>(vertices.Length);
for (int i = 0; i < vertices.Length; i++) {
var vert = vertices[i];
result[i] = vert.o + vert.v * s;
}
return result;
}
答案 0 :(得分:1)
我们应该检查发布配置中由.NET Standard 2.0编译器创建的IL。我本人不是IL专家,但我们只比较一下C#代码中查找两次和一次并使用dnSpy的自动注释。
首先,您的样本已经仅执行一次数组查找,并将该顶点存储在变量vert
中。这将创建以下IL输出;请注意,只有一条ldelem
指令,这意味着只有一个数组查找符合预期:
// loop start (head: IL_0037)
IL_000D: ldarg.0 // Loads the argument at index 0 onto the evaluation stack.
IL_000E: ldloc.1 // Loads the local variable at index 1 onto the evaluation stack.
IL_000F: ldelem ClassLibrary1.Vertex // Loads the element at a specified array index onto the top of the evaluation stack as the type specified in the instruction.
IL_0014: stloc.2 // Pops the current value from the top of the evaluation stack and stores it in a the local variable list at index 2.
IL_0015: ldloc.0 // Loads the local variable at index 0 onto the evaluation stack.
IL_0016: ldloc.1 // Loads the local variable at index 1 onto the evaluation stack.
IL_0017: ldloc.2 // Loads the local variable at index 2 onto the evaluation stack.
IL_0018: ldfld class [Syroot.Maths]Syroot.Maths.Vector2 ClassLibrary1.Vertex::o // Finds the value of a field in the object whose reference is currently on the evaluation stack.
IL_001D: ldloc.2 // Loads the local variable at index 2 onto the evaluation stack.
IL_001E: ldfld class [Syroot.Maths]Syroot.Maths.Vector2 ClassLibrary1.Vertex::v // Finds the value of a field in the object whose reference is currently on the evaluation stack.
IL_0023: ldarg.1 // Loads the argument at index 1 onto the evaluation stack.
IL_0024: call class [Syroot.Maths]Syroot.Maths.Vector2 [Syroot.Maths]Syroot.Maths.Vector2::op_Multiply(class [Syroot.Maths]Syroot.Maths.Vector2, float32) // Calls the method indicated by the passed method descriptor.
IL_0029: call class [Syroot.Maths]Syroot.Maths.Vector2 [Syroot.Maths]Syroot.Maths.Vector2::op_Addition(class [Syroot.Maths]Syroot.Maths.Vector2, class [Syroot.Maths]Syroot.Maths.Vector2) // Calls the method indicated by the passed method descriptor.
IL_002E: callvirt instance void class [netstandard]System.Collections.Generic.List`1<class [Syroot.Maths]Syroot.Maths.Vector2>::set_Item(int32, !0) // Calls a late-bound method on an object, pushing the return value onto the evaluation stack.
IL_0033: ldloc.1 // Loads the local variable at index 1 onto the evaluation stack.
IL_0034: ldc.i4.1 // Pushes the integer value of 1 onto the evaluation stack as an int32.
IL_0035: add // Adds two values and pushes the result onto the evaluation stack.
IL_0036: stloc.1 // Pops the current value from the top of the evaluation stack and stores it in a the local variable list at index 1.
IL_0037: ldloc.1 // Loads the local variable at index 1 onto the evaluation stack.
IL_0038: ldarg.0 // Loads the argument at index 0 onto the evaluation stack.
IL_0039: ldlen // Pushes the number of elements of a zero-based, one-dimensional array onto the evaluation stack.
IL_003A: conv.i4 // Converts the value on top of the evaluation stack to int32.
IL_003B: blt.s IL_000D // Transfers control to a target instruction (short form) if the first value is less than the second value.
// end loop
(我也一直在使用Syroot.Maths
库,因为我不知道您在使用什么,您的代码示例还不完整。)
现在,如果您的样本将执行多个数组查找,又名:
public static List<Vector2> Wavefronts(Vertex[] vertices, float s)
{
var result = new List<Vector2>(vertices.Length);
for (int i = 0; i < vertices.Length; i++)
{
result[i] = vertices[i].o + vertices[i].v * s;
}
return result;
}
...测试编译不会为我优化 ,它将进行两次数组查找(请注意两个ldelema
):
// loop start (head: IL_003B)
IL_000D: ldloc.0 // Loads the local variable at index 0 onto the evaluation stack.
IL_000E: ldloc.1 // Loads the local variable at index 1 onto the evaluation stack.
IL_000F: ldarg.0 // Loads the argument at index 0 onto the evaluation stack.
IL_0010: ldloc.1 // Loads the local variable at index 1 onto the evaluation stack.
IL_0011: ldelema ClassLibrary1.Vertex // Loads the address of the array element at a specified array index onto the top of the evaluation stack as type & (managed pointer).
IL_0016: ldfld class [Syroot.Maths]Syroot.Maths.Vector2 ClassLibrary1.Vertex::o // Finds the value of a field in the object whose reference is currently on the evaluation stack.
IL_001B: ldarg.0 // Loads the argument at index 0 onto the evaluation stack.
IL_001C: ldloc.1 // Loads the local variable at index 1 onto the evaluation stack.
IL_001D: ldelema ClassLibrary1.Vertex // Loads the address of the array element at a specified array index onto the top of the evaluation stack as type & (managed pointer).
IL_0022: ldfld class [Syroot.Maths]Syroot.Maths.Vector2 ClassLibrary1.Vertex::v // Finds the value of a field in the object whose reference is currently on the evaluation stack.
IL_0027: ldarg.1 // Loads the argument at index 1 onto the evaluation stack.
IL_0028: call class [Syroot.Maths]Syroot.Maths.Vector2 [Syroot.Maths]Syroot.Maths.Vector2::op_Multiply(class [Syroot.Maths]Syroot.Maths.Vector2, float32) // Calls the method indicated by the passed method descriptor.
IL_002D: call class [Syroot.Maths]Syroot.Maths.Vector2 [Syroot.Maths]Syroot.Maths.Vector2::op_Addition(class [Syroot.Maths]Syroot.Maths.Vector2, class [Syroot.Maths]Syroot.Maths.Vector2) // Calls the method indicated by the passed method descriptor.
IL_0032: callvirt instance void class [netstandard]System.Collections.Generic.List`1<class [Syroot.Maths]Syroot.Maths.Vector2>::set_Item(int32, !0) // Calls a late-bound method on an object, pushing the return value onto the evaluation stack.
IL_0037: ldloc.1 // Loads the local variable at index 1 onto the evaluation stack.
IL_0038: ldc.i4.1 // Pushes the integer value of 1 onto the evaluation stack as an int32.
IL_0039: add // Adds two values and pushes the result onto the evaluation stack.
IL_003A: stloc.1 // Pops the current value from the top of the evaluation stack and stores it in a the local variable list at index 1.
IL_003B: ldloc.1 // Loads the local variable at index 1 onto the evaluation stack.
IL_003C: ldarg.0 // Loads the argument at index 0 onto the evaluation stack.
IL_003D: ldlen // Pushes the number of elements of a zero-based, one-dimensional array onto the evaluation stack.
IL_003E: conv.i4 // Converts the value on top of the evaluation stack to int32.
IL_003F: blt.s IL_000D // Transfers control to a target instruction (short form) if the first value is less than the second value.
// end loop
为什么这种情况是我所不知道的。也许某些编译器专家可以澄清。也许仍然可以通过JIT对它进行优化。达米安(Damien)的评论(另一个主题,我不是:-)的专家)。