一个存储 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/