简体   繁体   English

使用数组索引的C#性能

[英]C# performance using array index

I have an array and I want to use on single value multiple times in the same method. 我有一个数组,我想在同一个方法中多次使用单值。

int[] array = new array[] {1, 2, 3};

Now I'm asking you if its a big performance issue if I use the array and an index to get the value... 现在我问你,如果我使用数组和索引获取值,它是否是一个很大的性能问题...

int x = array[1] * 2;
int y = array[1] * 3;
int z = array[1] * 4;

or is it better to create a local variable? 或者是否更好地创建局部变量?

int value = array[1];
int x = value * 2;
int y = value * 3;
int z = value * 4;

I know it easier to read with a local variable, but it just interested me if it makes any performace differences. 我知道使用局部变量更容易阅读,但它只是让我感兴趣,如果它产生任何性能差异。 ;-) ;-)

No, there would be no performance difference. 不,没有性能差异。 For this to work: 为此工作:

int x = array[1] * 2;

the value at array[1] is going to have to be moved into a memory location anyway when the IL is generated. 当生成IL时,无论如何都必须将array[1]的值移动到存储器位置。 The remaining operations will then be optimized away by the compiler (ie it's not going to retrieve the value more than once). 然后,编译器将优化剩余的操作(即,它不会多次检索该值)。


Alright, to settle the argument I decided to dump each, here is the first: 好吧,为了解决争论,我决定抛弃每一个,这里是第一个:

.method private hidebysig static void  Main(string[] args) cil managed
{
  IL_0000:  nop
  IL_0001:  ldc.i4.3
  IL_0002:  newarr     [mscorlib]System.Int32
  IL_0007:  dup
  IL_0008:  ldtoken    field valuetype '<PrivateImplementationDetails>{79A4FD92-FA37-4EB9-8056-B52A57262FBB}'/'__StaticArrayInitTypeSize=12' '<PrivateImplementationDetails>{79A4FD92-FA37-4EB9-8056-B52A57262FBB}'::'$$method0x6000001-1'
  IL_000d:  call       void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array,
                                                                                                      valuetype [mscorlib]System.RuntimeFieldHandle)
  IL_0012:  stloc.0
  IL_0013:  ldloc.0
  IL_0014:  ldc.i4.1
  IL_0015:  ldelem.i4
  IL_0016:  stloc.1
  IL_0017:  ldloc.1
  IL_0018:  ldc.i4.2
  IL_0019:  mul
  IL_001a:  stloc.2
  IL_001b:  ldloc.1
  IL_001c:  ldc.i4.3
  IL_001d:  mul
  IL_001e:  stloc.3
  IL_001f:  ldloc.1
  IL_0020:  ldc.i4.4
  IL_0021:  mul
  IL_0022:  stloc.s    z
  IL_0024:  ret
} // end of method Program::Main

and here is the second: 这是第二个:

.method private hidebysig static void  Main(string[] args) cil managed
{
  IL_0000:  nop
  IL_0001:  ldc.i4.3
  IL_0002:  newarr     [mscorlib]System.Int32
  IL_0007:  dup
  IL_0008:  ldtoken    field valuetype '<PrivateImplementationDetails>{79A4FD92-FA37-4EB9-8056-B52A57262FBB}'/'__StaticArrayInitTypeSize=12' '<PrivateImplementationDetails>{79A4FD92-FA37-4EB9-8056-B52A57262FBB}'::'$$method0x6000001-1'
  IL_000d:  call       void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array,
                                                                                                      valuetype [mscorlib]System.RuntimeFieldHandle)
  IL_0012:  stloc.0
  IL_0013:  ldloc.0
  IL_0014:  ldc.i4.1
  IL_0015:  ldelem.i4
  IL_0016:  stloc.1
  IL_0017:  ldloc.1
  IL_0018:  ldc.i4.2
  IL_0019:  mul
  IL_001a:  stloc.2
  IL_001b:  ldloc.1
  IL_001c:  ldc.i4.3
  IL_001d:  mul
  IL_001e:  stloc.3
  IL_001f:  ldloc.1
  IL_0020:  ldc.i4.4
  IL_0021:  mul
  IL_0022:  stloc.s    z
  IL_0024:  ret
} // end of method Program::Main

They are exactly the same--as I stated. 它们完全一样 - 正如我所说的那样。

Although I agree that such micro-optimizations are evil and unnecessary, and readability might be more important , it was quite a fun to make a dummy benchmark for two methods: 虽然我同意这种微观优化是邪恶和不必要的,并且可读性可能更重要 ,但为两种方法制作虚拟基准测试非常有趣:

private static int TestWithIndex(int[] array)
{
    int x = array[1] * 2;
    int y = array[1] * 3;
    int z = array[1] * 4;
    return x + y + z;
}

private static int TestWithTemp(int[] array)
{
    int value = array[1];
    int x = value * 2;
    int y = value * 3;
    int z = value * 4;
    return x + y + z;
}

calling them int.MaxValue times in Release mode produces: Release模式下调用它们int.MaxValue次数会产生:

  • 12032 ms - for TestWithIndex TestWithIndex ms - 适用于TestWithIndex
  • 10525 ms - for TestWithTemp 10525 ms - 适用于TestWithTemp

And then let's look at the IL generated (Release mode, Optimizations enabled): 然后让我们看看生成的IL(发布模式,启用了优化):

TestWithIndex TestWithIndex

.method private hidebysig static 
    int32 TestWithIndex (
        int32[] 'array'
    ) cil managed 
{
    // Method begins at RVA 0x2564
    // Code size 29 (0x1d)
    .maxstack 2
    .locals init (
        [0] int32 x,
        [1] int32 y,
        [2] int32 z,
        [3] int32 CS$1$0000
    )

    IL_0000: nop
    IL_0001: ldarg.0
    IL_0002: ldc.i4.1
    IL_0003: ldelem.i4
    IL_0004: ldc.i4.2
    IL_0005: mul
    IL_0006: stloc.0
    IL_0007: ldarg.0
    IL_0008: ldc.i4.1
    IL_0009: ldelem.i4
    IL_000a: ldc.i4.3
    IL_000b: mul
    IL_000c: stloc.1
    IL_000d: ldarg.0
    IL_000e: ldc.i4.1
    IL_000f: ldelem.i4
    IL_0010: ldc.i4.4
    IL_0011: mul
    IL_0012: stloc.2
    IL_0013: ldloc.0
    IL_0014: ldloc.1
    IL_0015: add
    IL_0016: ldloc.2
    IL_0017: add
    IL_0018: stloc.3
    IL_0019: br.s IL_001b

    IL_001b: ldloc.3
    IL_001c: ret
} // end of method Program::TestWithIndex

Here we see three ldelem.i4 . 在这里我们看到三个ldelem.i4

TestWithTemp TestWithTemp

.method private hidebysig static 
    int32 TestWithTemp (
        int32[] 'array'
    ) cil managed 
{
    // Method begins at RVA 0x2590
    // Code size 29 (0x1d)
    .maxstack 2
    .locals init (
        [0] int32 'value',
        [1] int32 x,
        [2] int32 y,
        [3] int32 z,
        [4] int32 CS$1$0000
    )

    IL_0000: nop
    IL_0001: ldarg.0
    IL_0002: ldc.i4.1
    IL_0003: ldelem.i4
    IL_0004: stloc.0
    IL_0005: ldloc.0
    IL_0006: ldc.i4.2
    IL_0007: mul
    IL_0008: stloc.1
    IL_0009: ldloc.0
    IL_000a: ldc.i4.3
    IL_000b: mul
    IL_000c: stloc.2
    IL_000d: ldloc.0
    IL_000e: ldc.i4.4
    IL_000f: mul
    IL_0010: stloc.3
    IL_0011: ldloc.1
    IL_0012: ldloc.2
    IL_0013: add
    IL_0014: ldloc.3
    IL_0015: add
    IL_0016: stloc.s CS$1$0000
    IL_0018: br.s IL_001a

    IL_001a: ldloc.s CS$1$0000
    IL_001c: ret
} // end of method Program::TestWithTemp

Here only one ldelem.i4 of course. 当然这里只有一个ldelem.i4

If you do not have performance requirements or performance problems, then your main goal is to write readable code which will be easy to maintain. 如果您没有性能要求或性能问题,那么您的主要目标是编写易于维护的可读代码。 It's easy to see duplication in your first example: 在第一个示例中很容易看到重复:

int x = array[1] * 2;
int y = array[1] * 3;
int z = array[1] * 4;

It has several issues. 它有几个问题。 First - the more you have duplicated code, the more code you have to support and the higher chance you will not modify one of code copies at some point of time. 首先 - 您拥有的代码越多,您需要支持的代码就越多,并且您在某个时间点修改代码副本的可能性就越大。 Second - duplication always means you have hidden knowledge in your code. 第二 - 重复总是意味着你在代码中隐藏了知识。 If some code is repeated then it has specific meaning, which you haven't made obvious. 如果重复某些代码,那么它具有特定的含义,你没有明白这一点。 Eg you have speed value in second item of array. 例如,你在第二项数组中有速度值。 Make this knowledge explicit: 明确这些知识:

int speed = array[1];
int x = speed * 2; // of course, magic numbers also should be replaced
int y = speed * 3;
int z = speed * 4;

And remember - premature optimization is evil. 请记住 - 过早优化是邪恶的。 Usually you have 20% of code which takes 80% of execution time. 通常你有20%的代码需要80%的执行时间。 There is high probability that your optimizations will not affect application performance. 您的优化很可能不会影响应用程序性能。 So, you should find these 20% first, and only then do optimizations (if they really needed). 所以,你应该首先找到这20%,然后才进行优化(如果真的需要)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM