如何在C#中初始化整数数组

时间:2012-12-20 21:15:13

标签: c# .net

  

可能重复:
  c# Leaner way of initializing int array

基本上我想知道是否有比下面显示的更有效的代码

    private static int[] GetDefaultSeriesArray(int size, int value)
    {
        int[] result = new int[size];
        for (int i = 0; i < size; i++)
        {
            result[i] = value;
        }
        return result;
    }

其中大小可以在10到150000之间变化。对于小型阵列不是问题,但应该有更好的方法来执行上述操作。 我正在使用VS2010(.NET 4.0)

6 个答案:

答案 0 :(得分:8)

C#/ CLR没有内置的方法来使用非默认值初始化数组。

如果您在每个项目的操作中进行衡量,那么您的代码效率会很高。

如果并行初始化大型数组的块,您可以获得更快的初始化。由于多线程操作的重要成本,这种方法需要仔细调整。

通过分析您的需求并可能完全删除整个初始化,可以获得更好的结果。即如果数组通常包含常量值,则可以实现某种COW(写入时复制)方法,其中对象最初没有后备数组,并且simpy返回常量值,写入要创建的元素(可能是部分)后备数组修改过的段。

较慢但更紧凑的代码(可能更容易阅读)是使用Enumerable.Repeat。请注意,ToArray将导致为大型阵列分配大量内存(也可能最终在LOH上进行分配) - High memory consumption with Enumerable.Range?

 var result = Enumerable.Repeat(value, size).ToArray();

答案 1 :(得分:4)

提高速度的一种方法是使用Array.Copy。它可以在较低级别工作,在该级别中,它可以批量分配更大的内存部分。

通过批处理分配,您最终可以将数组从一个部分复制到自身。

最重要的是,批次本身可以非常有效地平行。

这是我的初始代码。在我的机器(只有两个核心)上,样本数量为1000万个项目,我的速度提高了15%左右。您需要使用批量大小(尝试保持页面大小的倍数以保持其效率)将其调整为您拥有的项目的大小。对于较小的阵列,它最终几乎与您的代码完全相同,因为它不会填满第一批,但在这些情况下它也不会(显着)更糟。

private const int batchSize = 1048576;
private static int[] GetDefaultSeriesArray2(int size, int value)
{

    int[] result = new int[size];

    //fill the first batch normally
    int end = Math.Min(batchSize, size);
    for (int i = 0; i < end; i++)
    {
        result[i] = value;
    }

    int numBatches = size / batchSize;

    Parallel.For(1, numBatches, batch =>
    {
        Array.Copy(result, 0, result, batch * batchSize, batchSize);
    });

    //handle partial leftover batch
    for (int i = numBatches * batchSize; i < size; i++)
    {
        result[i] = value;
    }

    return result;
}

答案 2 :(得分:1)

提高性能的另一种方法是使用一种非常基本的技术:循环展开。

我已经编写了一些代码来初始化一个包含2000万个项目的数组,重复执行100次并计算平均值。在不展开循环的情况下,这需要大约44 MS。循环展开10,过程在23 MS完成。

 private void Looper()
        {
            int repeats = 100;
            float avg = 0;

            ArrayList times = new ArrayList();

            for (int i = 0; i < repeats; i++)
                times.Add(Time()); 

            Console.WriteLine(GetAverage(times)); //44

            times.Clear();

            for (int i = 0; i < repeats; i++)            
                times.Add(TimeUnrolled()); 

            Console.WriteLine(GetAverage(times)); //22

        }

 private float GetAverage(ArrayList times)
        {
            long total = 0;
            foreach (var item in times)
            {
                total += (long)item;
            }

            return total / times.Count;
        }

        private long Time()
        {
            Stopwatch sw = new Stopwatch();
            int size = 20000000;
            int[] result = new int[size];
            sw.Start();


            for (int i = 0; i < size; i++)
            {
                result[i] = 5;
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
            return sw.ElapsedMilliseconds;
        }

        private long TimeUnrolled()
        {
            Stopwatch sw = new Stopwatch();
            int size = 20000000;
            int[] result = new int[size];
            sw.Start();


            for (int i = 0; i < size; i += 10)
            {
                result[i] = 5;
                result[i + 1] = 5;
                result[i + 2] = 5;
                result[i + 3] = 5;
                result[i + 4] = 5;
                result[i + 5] = 5;
                result[i + 6] = 5;
                result[i + 7] = 5;
                result[i + 8] = 5;
                result[i + 9] = 5;
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
            return sw.ElapsedMilliseconds;
        }

答案 3 :(得分:0)

Enumerable.Repeat(value, size).ToArray();

答案 4 :(得分:0)

读取En​​umerable.Repeat比循环的ops标准慢20倍,我发现唯一可能提高速度的是

private static int[] GetDefaultSeriesArray(int size, int value)
{
    int[] result = new int[size];
    for (int i = 0; i < size; ++i)
    {
        result[i] = value;
    }
    return result;
}

注意i ++改为++ i。 i ++复制i,递增i,并返回原始值。 ++我只返回递增的值

答案 5 :(得分:-2)

正如已经提到的那样,您可以利用这样的并行处理:

int[] result = new int[size];
Parallel.ForEach(result, x => x = value);
return result;

抱歉,我没有时间对此进行性能测试(在这台机器上没有安装VS)但是如果你能做到并分享结果就会很棒。

编辑:根据评论,我仍然认为在性能方面它们是等价的,你可以尝试并行for循环:

Parallel.For(0, size, i => result[i] = value);