如何快速将数组初始化为-1?

3
我理解我不应该优化程序的每一个点,让我们假设我需要优化数组初始化。
因此,我编写了一个比较“for循环”和“Array.Clear”的程序。
using System;
using System.Diagnostics;

namespace TestArraysClear
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] a = new int[100000];
            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i < 10; i++)
            {
                sw.Reset(); 
                sw.Start();
                for (int j = 0; j < a.Length; j++)
                {
                    a[j] = 0;
                }
                sw.Stop();
                Console.WriteLine("for " + sw.ElapsedTicks);
                sw.Reset();
                sw.Start();
                Array.Clear(a, 0, a.Length);
                sw.Stop();
                Console.WriteLine("Array.Clear " + sw.ElapsedTicks);
            }
        }
    }
}

我的电脑上的输出:

for 1166
Array.Clear 80
for 1136
Array.Clear 91
for 1350
Array.Clear 71
for 1028
Array.Clear 72
for 962
Array.Clear 54
for 1185
Array.Clear 46
for 962
Array.Clear 55
for 1091
Array.Clear 55
for 988
Array.Clear 54
for 1046
Array.Clear 55

所以Array.Clearfor循环快大约20倍。但是Array.Clear会初始化为0。我能否用相同的性能将数组初始化为-1
更新:我不寻找一些“极端不安全”的代码。我正在寻找像Array.Clear这样简单的东西。我只是想知道.NET提供了快速的0初始化,但是.NET没有提供初始化其他值的选项。那么为什么.NET更喜欢“0”而不是“-1”呢?
更新:我想重置现有的数组。因此,我正在寻找类似于Array.Clear的东西,它将数组重置为-1,而不是0

2
你不应该在如此少的测试中就这么快地得出结论。你应该使用更大的数组(远远超过1000,但现在你还可以),进行更多的迭代(指数级别的迭代而不仅仅是10次),并对它们进行平均处理。 - Jeff Mercado
我想知道你在做什么,为什么将一个数组设置为相同的值(O(n)操作)实际上需要很长时间。你真正想做什么? - Mike Bailey
@JeffMercado 更新了代码,修复了数组大小,增加到100000 - Oleg Vazhnev
@javapowered:这很好,一个漂亮的圆整数。但是,您需要重复此测试的许多次迭代才能获得良好的基准测试结果。仅有10次迭代是不够的。 - Jeff Mercado
@MikeBantegui 交易。在大多数应用程序中,你花费500微秒或495微秒并没有什么区别。但在交易中,如果你花费了495微秒,你可能会赚钱;如果你花费了500微秒,你可能会亏钱。 - Oleg Vazhnev
显示剩余8条评论
4个回答

6

可能有一种方法可以通过未管理的数组(指针)来设置连续的内存块为相同的值(例如,将int“ -1”映射到其4个字节的内存中的值)。

这篇文章讨论了比使用托管for循环更快的方法(给出的示例是byte []数组): http://techmikael.blogspot.com/2009/12/filling-array-with-default-value.html

此外,在这里: What is the equivalent of memset in C#?

正如其他帖子所提到的那样,似乎做这件事有点极端,通常使用for循环初始化数组所需的速度不会成为问题。


呵呵...我正要添加一个int*的建议,你比我快。 - Marc Gravell
@Marc Gravell 哈哈,没问题 - 如果您想要的话,请随意编辑并添加代码示例。 - dodgy_coder
我不需要“极端”。我只是想知道.NET为我们提供了非常快速的“0初始化”方法,但.NET无法初始化为其他值。 “0”的好处是什么?为什么“0”比“-1”更好? - Oleg Vazhnev

2

我不知道这是否快速,但一种更干净的初始化数组的方式。

int[] a = Enumerable.Range(0, 100000).Select(s => -1).ToArray();

更新: 更确切地说

int[] a = Enumerable.Repeat(-1, 100000).ToArray();

1
我想这完全取决于你习惯什么。在任何情况下,那个符号混乱的东西对我来说都不如一个简单的“for”循环更加“清晰”或者更加“简洁”! - Cody Gray
+1 是的,最终这是个人选择。 Lambda表达式通常比迭代器更受青睐,可能是因为它更精确,但我仍然同意这是个人偏好。 - Asif Mushtaq
2
不,不,不。这里有一个“Repeat”方法可以做到这一点。所以int[] a = Enumerable.Repeat(-1, 100000).ToArray(); - Jeppe Stig Nielsen
Enumerable.Repeat 肯定比枚举数组慢。任何客观的单元测试都会表明这一点。 - Christian Findlay

1

最快的方法是使用静态初始化:

int[] a = {-1, -1, -1, -1, ...}

你可以编写一个程序为你生成源代码,例如:

var sb = new StringBuilder("int[] a = {");
for (int i = 0; i < 10000; ++i)
    sb.append(i != 10000 -1 ? "-1," : "-1");
sb.append("};");

这是一个有趣的解决方案。但是,如果经常调用这样的初始化,会消耗太多内存,从而增加GC的额外工作吗? - Oleg Vazhnev
1
这与GC无关,是编译器在构建时完成所有工作。根据变量a的类型(全局/静态或堆栈本地),实际上在运行时可能根本没有性能影响。 - Mahmoud Al-Qudsi
这与展开循环并没有太大的区别,但由于它分配了一个新数组,因此它非常与GC相关。问题是关于重置现有数组的。 - Marc Gravell
@Marc 好吧,标题是“如何初始化数组”...我想OP的问题不是很清楚。只有在编译器没有在某个数据段中在编译时生成数组时,它才像一个展开的循环。 - Mahmoud Al-Qudsi
@MahmoudAl-Qudsi 如果我只执行一次这个操作,那么节省时间就没有意义。但实际上我经常重复执行它。 - Oleg Vazhnev
这很可能是初始化数组的快速方法。编译器似乎会将数据放在一个名为<PrivateImplementationDetails>{some guid}的生成类的字段中。然后,它会生成调用RuntimeHelpers.InitializeArray的代码。 - Jeppe Stig Nielsen

0

没有这种事情 - 参见这个问题这个

疯狂的理论 - 如果该方法在非托管代码中实现,清除数组可能会更快。但请注意@JeffMercado的评论,需要更大的样本量。


Clear() 不会截断数组。这些不是动态数组,它们是原始数组。 - Mahmoud Al-Qudsi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接