Array.Clear()vs Buffer.BlockCopy()

时间:2017-05-18 19:02:28

标签: c#

背景

  

我必须经常清除固定长度的字节数组(例如:byte[4096])。

我的问题是寻求适用于通用用法的答案,但对于那些想知道这个问题源于何处的人:我目前正在优化byte[]的对象池。我需要在将它放回池中时将其擦干净。

我的问题

  

使用Array.Clear()方法清除数组或使用   Buffer.BlockCopy()方法用空白数据覆盖数组,   哪种方法会表现得更好?

示例代码

使用Buffer.BlockCopy清除字节数组

Buffer.BlockCopy(blankArray, 0, array, 0, 4096);

- VERSUS -

使用Array.Clear清除字节数组

Array.Clear(array, 0, 4096);

我的研究

public class Benchmark
{
    const int NUMBER_OF_ITERATIONS = 10000000;
    const int SIZE = 4096;
    static byte[] _blank = new byte[SIZE];
    static byte[] _array = new byte[SIZE];

    public static int ArrayClear()
    {
        for (int i = 0; i < NUMBER_OF_ITERATIONS; i++)
        {
            Array.Clear(_array, 0, SIZE);
        }

        return NUMBER_OF_ITERATIONS;
    }

    public static int BlockCopy()
    {
        for (int i = 0; i < NUMBER_OF_ITERATIONS; i++)
        {
            Buffer.BlockCopy(_blank, 0, _array, 0, SIZE);
        }

        return NUMBER_OF_ITERATIONS;
    }
}

用于运行基准的代码

public class Program
{
    static Func<int>[] labs =
    {
        Benchmark.BlockCopy,
        Benchmark.ArrayClear,
    };

    static void Main(string[] args)
    {
        for (int i = 0; i < labs.Length; i++)
        {
            long counter = 0;
            int[] gcCounters = new int[]
            { 
                GC.CollectionCount(0), 
                GC.CollectionCount(1), 
                GC.CollectionCount(2) 
            };

            Console.WriteLine(labs[i].Method.Name + "()");
            Stopwatch stopwatch = Stopwatch.StartNew(); // start benchmark
            counter = labs[i].Invoke();
            stopwatch.Stop(); // end of benchmark

            DisplayResults(gcCounters, stopwatch, counter);
        }

        Console.Write("\nPress any key to continue...");
        Console.ReadKey();
    }

    static void DisplayResults(int[] gcCounters, Stopwatch stopwatch, long counter)
    {
        Console.WriteLine(
            "Total elapsed time was {0:N2} seconds",
            (stopwatch.Elapsed.TotalMilliseconds / 1000)
        );
        Console.WriteLine(
            "Total garbage collection (generation 0) was {0} collections", 
            (GC.CollectionCount(0) - gcCounters[0]).ToString("N0")
        );
        Console.WriteLine(
            "Total garbage collection (generation 1) was {0:N0} collections", 
            (GC.CollectionCount(1) - gcCounters[1])
        );
        Console.WriteLine(
                "Total garbage collection (generation 2) was {0:N0} collections", 
                (GC.CollectionCount(2) - gcCounters[2])
            );
        if (counter > 0)
        {
            Console.WriteLine(
                "Average processing time per iteration took {0:N2} microseconds", 
                ((double)stopwatch.Elapsed.TotalMilliseconds * 1000 / counter)
            );
        }
    }
}

结果

BlockCopy()结果

 Total elapsed time was 3.22 seconds.
 Total garbage collection (generation 0) was 0 collections.
 Total garbage collection (generation 1) was 0 collections.
 Total garbage collection (generation 2) was 0 collections.
 Average processing time per iteration took 0.32 microseconds.

ArrayClear()结果

 Total elapsed time was 0.90 seconds.
 Total garbage collection (generation 0) was 0 collections.
 Total garbage collection (generation 1) was 0 collections.
 Total garbage collection (generation 2) was 0 collections.
 Average processing time per iteration took 0.09 microseconds.
  

似乎ArrayClear更快。我不确定这是否意味着它的表现也更好。

1 个答案:

答案 0 :(得分:5)

您的研究可以使您得出正确的结论:Array.Clear()在此特定情况下优于Buffer.BlockCopy()

BenchmarkDotNet

要运行此类基准测试,请使用BenchmarkDotNet。您可以使用BenchmarkDotNet简化和运行您的基准测试,如下所示:

using System;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

namespace Experiments
{
    [MemoryDiagnoser]
    public class Test
    {
        const int SIZE = 4096;
        static byte[] _blank = new byte[SIZE];
        static byte[] _array = new byte[SIZE];

        [Benchmark]
        public void ArrayClear()
        {
            Array.Clear(_array, 0, SIZE);
        }

        [Benchmark]
        public void BlockCopy()
        {
            Buffer.BlockCopy(_blank, 0, _array, 0, SIZE);
        }
    }

    public class Program
    {
        static void Main(string[] args)
        {
            BenchmarkRunner.Run<Test>();
        }
    }
}

结果

我在x86和x64下运行.NET Framework,在x64下运行.NET Core。以下结果表明,在所有实验中,Array.Clear()都优于Buffer.BlockCopy()

# Clr 4.0.30319.42000, 32bit LegacyJIT-v4.7.2053.0

     Method |        Mean |    StdDev | Allocated |
----------- |------------ |---------- |---------- |
 ArrayClear | 154.6503 ns | 0.0192 ns |       0 B |
  BlockCopy | 655.8208 ns | 0.0939 ns |       0 B |

# Clr 4.0.30319.42000, 64bit RyuJIT-v4.7.2053.0

     Method |        Mean |    StdDev | Allocated |
----------- |------------ |---------- |---------- |
 ArrayClear | 179.2065 ns | 0.0205 ns |       0 B |
  BlockCopy | 320.4117 ns | 0.0380 ns |       0 B |

# .NET Core 4.6.25211.01, 64bit RyuJIT

     Method |        Mean |    StdDev | Allocated |
----------- |------------ |---------- |---------- |
 ArrayClear | 107.5015 ns | 0.0145 ns |       0 B |
  BlockCopy | 221.3139 ns | 0.0449 ns |       0 B |