阵列与列表的性能


191

假设您需要一个整数列表/数组,并且需要经常迭代,而我的意思是非常频繁。原因可能会有所不同,但是说这是大批量处理最内层循环的核心。

通常,由于大小的灵活性,人们会选择使用列表(List)。最重要的是,msdn文档声称Lists在内部使用数组,并且执行速度应该一样快(对Reflector的快速浏览可以确认这一点)。没关系,这涉及一些开销。

有人实际测量过吗?遍历一个列表6M次与数组花费相同的时间吗?


3
除了性能问题之外,相对于列表,我更喜欢使用数组,因为它们具有固定大小(当然,在不需要更改项目数的情况下)。在阅读现有代码时,我发现快速了解某个项目被迫具有固定大小会有所帮助,而不必在功能中深入检查代码。
沃伦·史蒂文斯

2
T[]vs. List<T>可以带来很大的性能差异。我刚刚优化了一个(嵌套的)循环密集型应用程序,以在.NET 4.0上从列表移动到数组。我原本预期可能会提高5%到10%,但加速超过40%!除了直接从列表移动到数组外,没有其他更改。所有枚举都使用foreach语句完成。根据Marc Gravell的回答,看起来foreachwith List<T>特别糟糕。
Special Sauce 2015年

Answers:


221

非常容易测量...

在少数我知道长度是固定的紧闭处理代码中我使用数组来进行微优化。如果使用索引器/形式,数组的速度可能会快-但IIRC认为,这取决于数组中数据的类型。但是除非您需要进行微优化,否则请使其保持简单易用。List<T>

当然,这仅在您读取所有数据时适用;对于基于键的查找,字典将更快。

这是我使用“ int”的结果(第二个数字是一个校验和,以验证它们是否都完成了相同的工作):

(已编辑以修复错误)

List/for: 1971ms (589725196)
Array/for: 1864ms (589725196)
List/foreach: 3054ms (589725196)
Array/foreach: 1860ms (589725196)

根据测试装备:

using System;
using System.Collections.Generic;
using System.Diagnostics;
static class Program
{
    static void Main()
    {
        List<int> list = new List<int>(6000000);
        Random rand = new Random(12345);
        for (int i = 0; i < 6000000; i++)
        {
            list.Add(rand.Next(5000));
        }
        int[] arr = list.ToArray();

        int chk = 0;
        Stopwatch watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            int len = list.Count;
            for (int i = 0; i < len; i++)
            {
                chk += list[i];
            }
        }
        watch.Stop();
        Console.WriteLine("List/for: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            for (int i = 0; i < arr.Length; i++)
            {
                chk += arr[i];
            }
        }
        watch.Stop();
        Console.WriteLine("Array/for: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            foreach (int i in list)
            {
                chk += i;
            }
        }
        watch.Stop();
        Console.WriteLine("List/foreach: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            foreach (int i in arr)
            {
                chk += i;
            }
        }
        watch.Stop();
        Console.WriteLine("Array/foreach: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        Console.ReadLine();
    }
}

8
有趣的细节:这是我的装备(.net 3.5 sp1)上发布/调试的时间:0.92、0.80、0.96、0.93;这告诉我,虚拟机中存在一些智能,可以比其他情况更好地优化Array / for循环约10%。
David Schmitt,2009年

2
是的,有针对阵列/针对的JIT优化。实际上,我给人的印象是它包括 Length情况(因为它知道它是固定的),因此为什么我没有先将其拉出(与List所做的不同)。感谢更新。
马克·格雷夫

2
奇怪的。我非常相似的测试表明,使用数组时,for和foreach之间没有显着差异。将在博客文章中对基准进行彻底调查,其他人可以运行该基准,并将结果发送给我……
Jon Skeet

1
如果为每个测试使用不同的chk变量(chk1,chk2,chk3,chk4),我会得到截然不同的结果。List / for = 1303ms,Array / for = 433ms。有什么想法吗?
2013年

4
在上述评论中,乔恩(Jon)到乔恩·斯凯特(Jon Skeet)的博客的链接已断开。以下是更新的链接。codeblog.jonskeet.uk/2009/01/29/…–
乔什·德隆

87

摘要:

  • 数组需要使用:

    • 尽可能经常。它的速度很快,并且RAM范围最小,可以存储相同数量的信息。
    • 如果您知道所需的确切细胞数
    • 如果数据保存在数组中<85000 b(85000/32 = 2656个整数数据元素)
    • 如果需要,则随机访问速度较高
  • 清单需要使用:

    • 如果需要将单元格添加到列表末尾(通常)
    • 如果需要在列表的开头/中间添加单元格(不要经常)
    • 如果数据保存在数组中<85000 b(85000/32 = 2656个整数数据元素)
    • 如果需要,则随机访问速度较高
  • LinkedList需要使用:

    • 如果需要在列表的开头/中间/结尾添加单元格(通常)
    • 如果需要,仅顺序访问(向前/向后)
    • 如果您需要保存大项目,但项目数量很少。
    • 最好不要用于大量项目,因为它会为链接使用额外的内存。

更多细节:

颜色含义

数组vs列表vs链接列表

更多详细信息:

https://stackoverflow.com/a/29263914/4423545


您对列表前缀相对较快但插入速度较慢的说法感到有些困惑。插入也是线性时间,平均比插入时间快50%。
Mike Marynowski

1
c#列表中的@MikeMarynowski是Array的包装器。因此,在插入列表的情况下,您只有到某个时间的线性时间。之后,该系统将创建一个更大的新阵列,并且需要时间从旧阵列中复制项目。
安德鲁(Andrew)

相同的东西。
Mike Marynowski

前置操作只是在0处的插入。就性能而言,这是最坏的情况,因此,如果插入速度很慢,则前置操作甚至会更慢。
Mike Marynowski

插入和前缀都为O(n)(摊销)。前缀是一个插入,但它是最慢的插入,因为它必须将列表中的所有项目向上移动一个位置。随机位置中的插入只需要向上移动索引比插入点更高的项目,因此平均有50%的项目。
Mike Marynowski

26

我认为性能会非常相似。使用列表vs数组时涉及的开销是IMHO,当您向列表中添加项目时,以及当达到数组的容量时,列表必须增加其内部使用的数组的大小时。

假设您有一个容量为10的列表,那么一旦要添加第11个元素,该列表将增加其容量。您可以通过将列表的容量初始化为要容纳的项目数来降低性能影响。

但是,为了弄清楚遍历List和遍历数组一样快,为什么不对它进行基准测试呢?

int numberOfElements = 6000000;

List<int> theList = new List<int> (numberOfElements);
int[] theArray = new int[numberOfElements];

for( int i = 0; i < numberOfElements; i++ )
{
    theList.Add (i);
    theArray[i] = i;
}

Stopwatch chrono = new Stopwatch ();

chrono.Start ();

int j;

 for( int i = 0; i < numberOfElements; i++ )
 {
     j = theList[i];
 }

 chrono.Stop ();
 Console.WriteLine (String.Format("iterating the List took {0} msec", chrono.ElapsedMilliseconds));

 chrono.Reset();

 chrono.Start();

 for( int i = 0; i < numberOfElements; i++ )
 {
     j = theArray[i];
 }

 chrono.Stop ();
 Console.WriteLine (String.Format("iterating the array took {0} msec", chrono.ElapsedMilliseconds));

 Console.ReadLine();

在我的系统上;在阵列上进行迭代需要33毫秒;遍历列表花费了66毫秒。

老实说,我没想到变化会那么大。因此,我将迭代放入一个循环中:现在,我两次都执行了1000次。结果是:

迭代List花费67146毫秒迭代数组花费40821毫秒

现在,变化不再那么大,但是...

因此,我启动了.NET Reflector,并且List类的索引器的获取程序如下所示:

public T get_Item(int index)
{
    if (index >= this._size)
    {
        ThrowHelper.ThrowArgumentOutOfRangeException();
    }
    return this._items[index];
}

如您所见,当您使用列表的索引器时,列表将执行检查是否不超出内部数组的范围。这张额外的支票需要付费。


嗨,弗雷德里克,谢谢!您将如何解释您的列表花费了两倍的时间。不是您所期望的。您是否尝试增加元素数量?
波阿斯

1
不会返回this._items [index]; 如果索引超出范围,已经抛出异常了吗?当有或没有最终结果时,.NET为什么要进行此额外检查?
John Mercier

@John Mercier检查的是列表的大小(当前包含的项目数),该大小不同于_items数组的容量,并且可能小于_items数组的容量。该阵列分配有多余的容量,无需每次分配都重新分配,从而可以更快地添加将来的项目。
Trasvi 2015年

21

如果您只是从其中一个(而不是循环)中获取单个值,那么两个都将进行边界检查(请记住您在托管代码中),只是列表执行了两次。稍后请参阅注释,以了解为什么这没什么大不了的。

如果您使用自己的for(int int i = 0; i <x。[Length / Count]; i ++),则主要区别如下:

  • 数组:
    • 边界检查已删除
  • 清单
    • 边界检查

如果使用的是foreach,则主要区别如下:

  • 数组:
    • 没有分配对象来管理迭代
    • 边界检查已删除
  • 通过已知为List的变量进行列表。
    • 迭代管理变量是堆栈分配的
    • 边界检查
  • 通过已知为IList的变量进行列表。
    • 迭代管理变量是堆分配的
    • 边界检查也会执行,列表值在foreach期间可能不会更改,而数组可以更改。

边界检查通常没什么大不了的(特别是如果您使用的CPU具有较深的流水线和分支预测能力-这是当今大多数时间的标准),但是只有您自己的分析才能告诉您这是否是一个问题。如果您在代码中避免堆分配的部分(好的示例是库或在哈希码实现中),则确保将变量键入为List not IList可以避免这种陷阱。一如既往地介绍是否重要。


11

[ 另请参阅此问题 ]

我已经修改了马克的答案以使用实际的随机数,并且在所有情况下实际上都执行相同的工作。

结果:

         对于foreach
数组:1575ms 1575ms(+ 0%)
清单:1630ms 2627ms(+ 61%)
         (+ 3%)(+ 67%)

(校验和:-1000038876)

在VS 2008 SP1下编译为Release。在Q6600@2.40GHz,.NET 3.5 SP1上无需调试即可运行。

码:

class Program
{
    static void Main(string[] args)
    {
        List<int> list = new List<int>(6000000);
        Random rand = new Random(1);
        for (int i = 0; i < 6000000; i++)
        {
            list.Add(rand.Next());
        }
        int[] arr = list.ToArray();

        int chk = 0;
        Stopwatch watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            int len = list.Count;
            for (int i = 0; i < len; i++)
            {
                chk += list[i];
            }
        }
        watch.Stop();
        Console.WriteLine("List/for: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            int len = arr.Length;
            for (int i = 0; i < len; i++)
            {
                chk += arr[i];
            }
        }
        watch.Stop();
        Console.WriteLine("Array/for: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            foreach (int i in list)
            {
                chk += i;
            }
        }
        watch.Stop();
        Console.WriteLine("List/foreach: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            foreach (int i in arr)
            {
                chk += i;
            }
        }
        watch.Stop();
        Console.WriteLine("Array/foreach: {0}ms ({1})", watch.ElapsedMilliseconds, chk);
        Console.WriteLine();

        Console.ReadLine();
    }
}

这很奇怪-我只运行了使用/ o + / debug-从命令行(3.5SP1)构建的确切代码,我的结果是:list / for:1524; 阵列/用于:1472; 清单/ foreach:4128; 数组/ foreach:1484。
乔恩·斯基特

您说这是作为发行版编译的-我可以检查一下它是否在运行而不是调试吗?我知道这是一个愚蠢的问题,但是我无法以其他方式解释结果……
乔恩·斯基特

2

测量结果不错,但是根据您在内部循环中所做的工作,您将获得截然不同的结果。衡量自己的情况。如果您使用的是多线程,那么仅此一项就很重要。



2

这是使用IEnumerable字典的一种:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

static class Program
{
    static void Main()
    {
        List<int> list = new List<int>(6000000);

        for (int i = 0; i < 6000000; i++)
        {
                list.Add(i);
        }
        Console.WriteLine("Count: {0}", list.Count);

        int[] arr = list.ToArray();
        IEnumerable<int> Ienumerable = list.ToArray();
        Dictionary<int, bool> dict = list.ToDictionary(x => x, y => true);

        int chk = 0;
        Stopwatch watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            int len = list.Count;
            for (int i = 0; i < len; i++)
            {
                chk += list[i];
            }
        }
        watch.Stop();
        Console.WriteLine("List/for: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            for (int i = 0; i < arr.Length; i++)
            {
                chk += arr[i];
            }
        }
        watch.Stop();
        Console.WriteLine("Array/for: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            foreach (int i in Ienumerable)
            {
                chk += i;
            }
        }

        Console.WriteLine("Ienumerable/for: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            foreach (int i in dict.Keys)
            {
                chk += i;
            }
        }

        Console.WriteLine("Dict/for: {0}ms ({1})", watch.ElapsedMilliseconds, chk);


        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            foreach (int i in list)
            {
                chk += i;
            }
        }

        watch.Stop();
        Console.WriteLine("List/foreach: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            foreach (int i in arr)
            {
                chk += i;
            }
        }
        watch.Stop();
        Console.WriteLine("Array/foreach: {0}ms ({1})", watch.ElapsedMilliseconds, chk);



        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            foreach (int i in Ienumerable)
            {
                chk += i;
            }
        }
        watch.Stop();
        Console.WriteLine("Ienumerable/foreach: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 100; rpt++)
        {
            foreach (int i in dict.Keys)
            {
                chk += i;
            }
        }
        watch.Stop();
        Console.WriteLine("Dict/foreach: {0}ms ({1})", watch.ElapsedMilliseconds, chk);

        Console.ReadLine();
    }
}

2

不要尝试通过增加元素数量来增加容量。

性能

List For Add: 1ms
Array For Add: 2397ms

    Stopwatch watch;
        #region --> List For Add <--

        List<int> intList = new List<int>();
        watch = Stopwatch.StartNew();
        for (int rpt = 0; rpt < 60000; rpt++)
        {
            intList.Add(rand.Next());
        }
        watch.Stop();
        Console.WriteLine("List For Add: {0}ms", watch.ElapsedMilliseconds);
        #endregion

        #region --> Array For Add <--

        int[] intArray = new int[0];
        watch = Stopwatch.StartNew();
        int sira = 0;
        for (int rpt = 0; rpt < 60000; rpt++)
        {
            sira += 1;
            Array.Resize(ref intArray, intArray.Length + 1);
            intArray[rpt] = rand.Next();

        }
        watch.Stop();
        Console.WriteLine("Array For Add: {0}ms", watch.ElapsedMilliseconds);

        #endregion

我要调整数组大小60k的速度将会很慢...不过,在实际使用中,方法肯定是检查您需要多少个额外的插槽,将其调整为长度+ 60k,然后遍历插入片段。
tobriand

如果每次发现需要更多空间时将大小加倍,则调整数组大小的速度将非常快。我发现这样做似乎和在初始声明后重新调整大小的时间差不多。这为您提供了列表的灵活性和阵列的大部分速度。
user1318499 '18

2

我担心其他答案中发布的基准仍然会为编译器留出空间来优化,消除或合并循环,因此我写道:

  • 使用了不可预测的输入(随机)
  • 运行计算结果并打印到控制台
  • 每次重复修改输入数据

结果是直接数组的性能比访问包装在IList中的数组要好250%:

  • 10亿个阵列访问:4000毫秒
  • 10亿个列表访问时间:10000毫秒
  • 1亿个阵列访问:350毫秒
  • 1亿个列表访问时间:1000毫秒

这是代码:

static void Main(string[] args) {
  const int TestPointCount = 1000000;
  const int RepetitionCount = 1000;

  Stopwatch arrayTimer = new Stopwatch();
  Stopwatch listTimer = new Stopwatch();

  Point2[] points = new Point2[TestPointCount];
  var random = new Random();
  for (int index = 0; index < TestPointCount; ++index) {
    points[index].X = random.NextDouble();
    points[index].Y = random.NextDouble();
  }

  for (int repetition = 0; repetition <= RepetitionCount; ++repetition) {
    if (repetition > 0) { // first repetition is for cache warmup
      arrayTimer.Start();
    }
    doWorkOnArray(points);
    if (repetition > 0) { // first repetition is for cache warmup
      arrayTimer.Stop();
    }

    if (repetition > 0) { // first repetition is for cache warmup
      listTimer.Start();
    }
    doWorkOnList(points);
    if (repetition > 0) { // first repetition is for cache warmup
      listTimer.Stop();
    }
  }

  Console.WriteLine("Ignore this: " + points[0].X + points[0].Y);
  Console.WriteLine(
    string.Format(
      "{0} accesses on array took {1} ms",
      RepetitionCount * TestPointCount, arrayTimer.ElapsedMilliseconds
    )
  );
  Console.WriteLine(
    string.Format(
      "{0} accesses on list took {1} ms",
      RepetitionCount * TestPointCount, listTimer.ElapsedMilliseconds
    )
  );

}

private static void doWorkOnArray(Point2[] points) {
  var random = new Random();

  int pointCount = points.Length;

  Point2 accumulated = Point2.Zero;
  for (int index = 0; index < pointCount; ++index) {
    accumulated.X += points[index].X;
    accumulated.Y += points[index].Y;
  }

  accumulated /= pointCount;

  // make use of the result somewhere so the optimizer can't eliminate the loop
  // also modify the input collection so the optimizer can merge the repetition loop
  points[random.Next(0, pointCount)] = accumulated;
}

private static void doWorkOnList(IList<Point2> points) {
  var random = new Random();

  int pointCount = points.Count;

  Point2 accumulated = Point2.Zero;
  for (int index = 0; index < pointCount; ++index) {
    accumulated.X += points[index].X;
    accumulated.Y += points[index].Y;
  }

  accumulated /= pointCount;

  // make use of the result somewhere so the optimizer can't eliminate the loop
  // also modify the input collection so the optimizer can merge the repetition loop
  points[random.Next(0, pointCount)] = accumulated;
}

0

由于List <>在内部使用数组,因此基本性能应该相同。列表可能会变慢的两个原因:

  • 要在列表中查找元素,将调用List方法,该方法将在基础数组中进行查找。因此,您需要在那里进行其他方法调用。另一方面,编译器可能会认识到这一点并优化“不必要”的调用。
  • 如果编译器知道数组的大小,而对于未知长度的列表则无法执行,则编译器可能会进行一些特殊的优化。如果列表中只有几个元素,则可能会带来一些性能改进。

要检查它是否对您有所帮助,最好将已发布的计时功能调整为计划使用的大小列表,并查看特殊情况的结果。


0

因为我有类似的问题,所以这使我快速入门。

我的问题更加具体,“什么是自反数组实现的最快方法”

Marc Gravell进行的测试显示了很多,但是访问时间却不完全相同。他的时间安排还包括遍历数组和列表。由于我还提出了我想测试的第三种方法,即“字典”,只是为了进行比较,因此我扩展了hist测试代码。

首先,我使用一个常数进行测试,这给了我一定的时间,包括循环。这是一个“裸机”时间,不包括实际访问权限。然后,我对访问主题结构进行了测试,这为我提供了“包括开销”的计时,循环和实际访问权限。

“裸露”时序与“开销包含”时序之间的差异为我指示了“结构访问”时序。

但是这个时间有多准确?在测试期间,窗户将进行切片以进行抽切。我没有时间分片的信息,但我认为它在测试过程中分布均匀,大约为几十毫秒,这意味着计时的精度应该在+/- 100毫秒左右。有点粗略的估计?无论如何,都是系统测量误差的来源。

此外,测试是在“调试”模式下进行的,没有进行优化。否则,编译器可能会更改实际的测试代码。

因此,我得到了两个结果,一个是标记为“(c)”的常量,另一个是标记为“(n)”的访问权,差值“ dt”告诉我实际访问需要多少时间。

结果如下:

          Dictionary(c)/for: 1205ms (600000000)
          Dictionary(n)/for: 8046ms (589725196)
 dt = 6841

                List(c)/for: 1186ms (1189725196)
                List(n)/for: 2475ms (1779450392)
 dt = 1289

               Array(c)/for: 1019ms (600000000)
               Array(n)/for: 1266ms (589725196)
 dt = 247

 Dictionary[key](c)/foreach: 2738ms (600000000)
 Dictionary[key](n)/foreach: 10017ms (589725196)
 dt = 7279

            List(c)/foreach: 2480ms (600000000)
            List(n)/foreach: 2658ms (589725196)
 dt = 178

           Array(c)/foreach: 1300ms (600000000)
           Array(n)/foreach: 1592ms (589725196)
 dt = 292


 dt +/-.1 sec   for    foreach
 Dictionary     6.8       7.3
 List           1.3       0.2
 Array          0.2       0.3

 Same test, different system:
 dt +/- .1 sec  for    foreach
 Dictionary     14.4   12.0
       List      1.7    0.1
      Array      0.5    0.7

通过对时序误差进行更好的估计(如何消除由于时间分割而导致的系统测量误差?),可以对结果进行更多的说明。

列表/ foreach的访问速度似乎最快,但是开销却使它丧命。

List / for和List / foreach之间的区别是固定的。也许涉及一些兑现?

此外,对于访问数组,使用for循环还是foreach循环都没关系。计时结果及其准确性使结果“可比”。

到目前为止,使用字典是最慢的,我只考虑了它,因为在左侧(索引器),我有一个稀疏的整数列表,而不是此测试中使用的范围。

这是修改后的测试代码。

Dictionary<int, int> dict = new Dictionary<int, int>(6000000);
List<int> list = new List<int>(6000000);
Random rand = new Random(12345);
for (int i = 0; i < 6000000; i++)
{
    int n = rand.Next(5000);
    dict.Add(i, n);
    list.Add(n);
}
int[] arr = list.ToArray();

int chk = 0;
Stopwatch watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    int len = dict.Count;
    for (int i = 0; i < len; i++)
    {
        chk += 1; // dict[i];
    }
}
watch.Stop();
long c_dt = watch.ElapsedMilliseconds;
Console.WriteLine("         Dictionary(c)/for: {0}ms ({1})", c_dt, chk);

chk = 0;
watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    int len = dict.Count;
    for (int i = 0; i < len; i++)
    {
        chk += dict[i];
    }
}
watch.Stop();
long n_dt = watch.ElapsedMilliseconds;
Console.WriteLine("         Dictionary(n)/for: {0}ms ({1})", n_dt, chk);
Console.WriteLine("dt = {0}", n_dt - c_dt);

watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    int len = list.Count;
    for (int i = 0; i < len; i++)
    {
        chk += 1; // list[i];
    }
}
watch.Stop();
c_dt = watch.ElapsedMilliseconds;
Console.WriteLine("               List(c)/for: {0}ms ({1})", c_dt, chk);

watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    int len = list.Count;
    for (int i = 0; i < len; i++)
    {
        chk += list[i];
    }
}
watch.Stop();
n_dt = watch.ElapsedMilliseconds;
Console.WriteLine("               List(n)/for: {0}ms ({1})", n_dt, chk);
Console.WriteLine("dt = {0}", n_dt - c_dt);

chk = 0;
watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    for (int i = 0; i < arr.Length; i++)
    {
        chk += 1; // arr[i];
    }
}
watch.Stop();
c_dt = watch.ElapsedMilliseconds;
Console.WriteLine("              Array(c)/for: {0}ms ({1})", c_dt, chk);

chk = 0;
watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    for (int i = 0; i < arr.Length; i++)
    {
        chk += arr[i];
    }
}
watch.Stop();
n_dt = watch.ElapsedMilliseconds;
Console.WriteLine("Array(n)/for: {0}ms ({1})", n_dt, chk);
Console.WriteLine("dt = {0}", n_dt - c_dt);

chk = 0;
watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    foreach (int i in dict.Keys)
    {
        chk += 1; // dict[i]; ;
    }
}
watch.Stop();
c_dt = watch.ElapsedMilliseconds;
Console.WriteLine("Dictionary[key](c)/foreach: {0}ms ({1})", c_dt, chk);

chk = 0;
watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    foreach (int i in dict.Keys)
    {
        chk += dict[i]; ;
    }
}
watch.Stop();
n_dt = watch.ElapsedMilliseconds;
Console.WriteLine("Dictionary[key](n)/foreach: {0}ms ({1})", n_dt, chk);
Console.WriteLine("dt = {0}", n_dt - c_dt);

chk = 0;
watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    foreach (int i in list)
    {
        chk += 1; // i;
    }
}
watch.Stop();
c_dt = watch.ElapsedMilliseconds;
Console.WriteLine("           List(c)/foreach: {0}ms ({1})", c_dt, chk);

chk = 0;
watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    foreach (int i in list)
    {
        chk += i;
    }
}
watch.Stop();
n_dt = watch.ElapsedMilliseconds;
Console.WriteLine("           List(n)/foreach: {0}ms ({1})", n_dt, chk);
Console.WriteLine("dt = {0}", n_dt - c_dt);

chk = 0;
watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    foreach (int i in arr)
    {
        chk += 1; // i;
    }
}
watch.Stop();
c_dt = watch.ElapsedMilliseconds;
Console.WriteLine("          Array(c)/foreach: {0}ms ({1})", c_dt, chk);

chk = 0;
watch = Stopwatch.StartNew();
for (int rpt = 0; rpt < 100; rpt++)
{
    foreach (int i in arr)
    {
        chk += i;
    }
}
watch.Stop();
n_dt = watch.ElapsedMilliseconds;
Console.WriteLine("Array(n)/foreach: {0}ms ({1})", n_dt, chk);
Console.WriteLine("dt = {0}", n_dt - c_dt);

0

在一些简短的测试中,我发现将两者结合起来可以更好地使用所谓的密集数学:

类型: List<double[]>

时间:00:00:05.1861300

类型: List<List<double>>

时间:00:00:05.7941351

类型: double[rows * columns]

时间:00:00:06.0547118

运行代码:

int rows = 10000;
int columns = 10000;

IMatrix Matrix = new IMatrix(rows, columns);

Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();


for (int r = 0; r < Matrix.Rows; r++)
    for (int c = 0; c < Matrix.Columns; c++)
        Matrix[r, c] = Math.E;

for (int r = 0; r < Matrix.Rows; r++)
    for (int c = 0; c < Matrix.Columns; c++)
        Matrix[r, c] *= -Math.Log(Math.E);


stopwatch.Stop();
TimeSpan ts = stopwatch.Elapsed;

Console.WriteLine(ts.ToString());

我确实希望我们拥有一些一流的硬件加速矩阵类,例如.NET团队已经完成了System.Numerics.Vectors该类!

C#可能是最好的ML语言,在此领域还有更多工作要做!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.