c# - C# 中数组/列表的子集迭代器,没有 yield

标签 c# c++ performance

一个存储 std::vector<int> 的类在内部可以使用子集索引轻松公开子集迭代器,如下所示:

开始:

return _data.begin() + _subset_begin;

结束:

return _data.begin() + _subset_end;

在 C# 中是否有一种有效的方法来执行此操作?我最初基于 yield 的实现非常缓慢。

for(int i = _subset_begin; i < _subset_end; ++i)
{
    yield return _data[i];
}

如何有效地解决这个问题?

我知道这并不完全相同,因为 C++ 使用迭代器作为返回类型,而 C# 使用 IEnum;但这些是每种语言的约定...

最佳答案

如果您使用的是 Linq,那么使用原始数组还是包装数组实际上差别不大。

为了对此进行测试,我编写了以下程序:

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

namespace Demo
{
    public static class Program
    {
        private static void Main()
        {
            var sw = new Stopwatch();

            int[] array = new int[1000000];

            int loops = 100;

            for (int trial = 0; trial < 4; ++trial)
            {
                sw.Restart();

                for (int i = 0; i < loops; ++i)
                    subsetViaArraySegment(array, 0, array.Length).Sum();

                Console.WriteLine("subsetViaArraySegment() took " + sw.Elapsed);
                sw.Restart();

                for (int i = 0; i < loops; ++i)
                    subsetViaYield(array, 0, array.Length).Sum();

                Console.WriteLine("subsetViaYield() took " + sw.Elapsed);
                sw.Restart();

                for (int i = 0; i < loops; ++i)
                    array.Sum();

                Console.WriteLine("Simple Sum() took " + sw.Elapsed);
                sw.Restart();

                for (int i = 0; i < loops; ++i)
                {
                    int total = 0;

                    for (int j = 0, n = array.Length; j < n; ++j)
                    {
                        unchecked
                        {
                            total += array[j];
                        }
                    }
                }

                Console.WriteLine("Inline code took " + sw.Elapsed);
                Console.WriteLine("");
            }
        }

        private static IEnumerable<int> subsetViaYield(int[] source, int start, int count)
        {
            for (int i = start, n = start + count; i < n; ++i)
                yield return source[i];
        }

        private static IEnumerable<int> subsetViaArraySegment(int[] source, int start, int count)
        {
            return new ArraySegment<int>(source, start, count);
        }
    }
}

它测试使用 Linq 使用 ArraySegment、yield 实现和原始数组本身对大型数组中的所有整数求和需要多长时间。它还在根本不使用 Linq 的情况下进行内联计算。

在 Windows 8.1 上运行 x64 RELEASE 构建的结果如下:

subsetViaArraySegment() took 00:00:00.6924651
subsetViaYield() took 00:00:00.9207855
Simple Sum() took 00:00:00.9876048
Inline code took 00:00:00.0884620

subsetViaArraySegment() took 00:00:01.0222854
subsetViaYield() took 00:00:00.9309415
Simple Sum() took 00:00:01.0031804
Inline code took 00:00:00.0890534

subsetViaArraySegment() took 00:00:01.0146586
subsetViaYield() took 00:00:00.9129277
Simple Sum() took 00:00:00.9842326
Inline code took 00:00:00.0890593

subsetViaArraySegment() took 00:00:01.0306027
subsetViaYield() took 00:00:00.9353762
Simple Sum() took 00:00:00.9902355
Inline code took 00:00:00.0879321

请注意 Linq 方法如何花费相似的时间 - 而内联代码要快很多倍。

因此,如果您确实需要最快的代码,则不应使用 Linq。相反,您可以使用 ArraySegment 来包装数组,然后编写使用数组段定义的子集的代码。

例如,计算整数数组段之和的函数可能如下所示:

public static long Sum(ArraySegment<int> arraySegment)
{
    long total = 0;
    var array = arraySegment.Array;

    for (int i = arraySegment.Offset, n = i + arraySegment.Count; i < n; ++i)
        total += array[i];

    return total;
}

关于c# - C# 中数组/列表的子集迭代器,没有 yield,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28432158/

相关文章:

matlab - 有效地将数据拆分为 bin

c# - 使用图形 API 添加 MS Teams 网站选项卡错误

c# - 它是否糟糕 - 使用 Linq to SQL 进行缓存的简单 DAL

c# - 可以使用具有正确实现的比较器的 SortedList<>/SortedDictionary<> 来保证插入顺序吗?

C++ : lvalue required as unary ‘&’ operand

c++ - 了解从二进制文件中提取频率以创建哈夫曼树的逻辑

C# 和转换泛型参数

c++ - 如何在 PlaySound 中将字符串作为 WAV 的名称传递?

varchar 上的 SQL 索引

java - 碧 Jade 报告 : fillReport very slow