基本上我想知道是否有比下面显示的更有效的代码
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)
答案 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)
读取Enumerable.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);