[英]Most efficient way of getting the N last element of an array
对于一个项目,我将不得不经常使用包含大量数据的数组的最后 N 个元素。
我试着做
myArray.Skip(myArray.Length - toTake).Take(toTake)
但我发现它很慢。
我将其与此进行了比较:
public static int[] TakeLast(this int[] inputArray, int count)
{
int[] returnArray = new int[count];
int startIndex = Math.Max(inputArray.Count() - count, 0);
unsafe
{
fixed (int* itemArrayPtr = &(inputArray[startIndex]))
{
fixed (int* arrayPtr = &(returnArray[0]))
{
int* itemValuePtr = itemArrayPtr;
int* valuePtr = arrayPtr;
for (int i = 0; i < count; i++)
{
*valuePtr++ = *itemValuePtr++;
}
}
}
}
return returnArray;
}
这很有效,因为它不能是通用的(我希望这适用于任何原始类型(int、float、double 等)。
有没有办法实现具有通用/linq/... 方法的可比性能? 我不需要让它在 IEnumerable 上工作,Array 对我来说就足够了。
编辑我目前正在测试你给我的所有方法,现在它是 Array.Copy 似乎更快:
Generating array for 100000000 elements.
SkipTake: 00:00:00.3009047
Unsafe: 00:00:00.0006289
Array.Copy: 00:00:00.0000012
Buffer.BlockCopy: 00:00:00.0001860
Reverse Linq: 00:00:00.2201143
Finished
来自评论:
public static T[] TakeLast<T>(this T[] inputArray, int count)
{
var result = new T[count];
Array.Copy(inputArray, inputArray.Length - count, result, 0, count);
return result;
}
似乎表现不错。 值得指出的是,根据具体需求,可能可以完全避免新数组,并迭代原始inputArray
。 你不能复制比根本不复制更快。 :)
这个怎么样? 应该相当快:
public static class ArrayExt
{
public static T[] TakeLast<T>(this T[] inputArray, int count) where T: struct
{
count = Math.Min(count, inputArray.Length);
int size = Marshal.SizeOf(typeof(T));
T[] result = new T[count];
Buffer.BlockCopy(inputArray, (inputArray.Length-count)*size, result, 0, count*size);
return result;
}
}
(我认为这比原始类型的 Array.Copy() 快一点。但我不会认为这是理所当然的 - 5 分钟后返回一些时间。;)
[编辑] 计时显示 Array.Copy() 具有相似的速度,但结果因运行而异并取决于数组大小。
这是一些示例代码:
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
namespace Demo
{
internal class Program
{
private void run()
{
const int ARRAY_SIZE = 10000;
var array = Enumerable.Range(0, ARRAY_SIZE).Select(x => x).ToArray();
Stopwatch sw = new Stopwatch();
const int COUNT = 100000;
for (int i = 0; i < 8; ++i)
{
sw.Restart();
for (int j = 0; j < COUNT; ++j)
array.TakeLastViaArrayCopy(ARRAY_SIZE/2);
Console.WriteLine("TakeLastViaArrayCopy took " + sw.Elapsed);
sw.Restart();
for (int j = 0; j < COUNT; ++j)
array.TakeLastViaBlockCopy(ARRAY_SIZE/2);
Console.WriteLine("TakeLastViaBlockCopy took " + sw.Elapsed);
Console.WriteLine();
}
}
private static void Main()
{
new Program().run();
}
}
public static class ArrayExt
{
public static T[] TakeLastViaBlockCopy<T>(this T[] inputArray, int count) where T: struct
{
count = Math.Min(count, inputArray.Length);
int size = Marshal.SizeOf(typeof(T));
T[] result = new T[count];
Buffer.BlockCopy(inputArray, (inputArray.Length-count)*size, result, 0, count*size);
return result;
}
public static T[] TakeLastViaArrayCopy<T>(this T[] inputArray, int count) where T: struct
{
count = Math.Min(count, inputArray.Length);
T[] result = new T[count];
Array.Copy(inputArray, inputArray.Length-count, result, 0, count);
return result;
}
}
}
结果(像往常一样发布构建):
TakeLastViaArrayCopy took 00:00:00.3028503
TakeLastViaBlockCopy took 00:00:00.3052196
TakeLastViaArrayCopy took 00:00:00.2969425
TakeLastViaBlockCopy took 00:00:00.3000117
TakeLastViaArrayCopy took 00:00:00.2906120
TakeLastViaBlockCopy took 00:00:00.2987753
TakeLastViaArrayCopy took 00:00:00.2954674
TakeLastViaBlockCopy took 00:00:00.3005010
TakeLastViaArrayCopy took 00:00:00.2944490
TakeLastViaBlockCopy took 00:00:00.3006893
TakeLastViaArrayCopy took 00:00:00.3041998
TakeLastViaBlockCopy took 00:00:00.2920206
TakeLastViaArrayCopy took 00:00:00.3115137
TakeLastViaBlockCopy took 00:00:00.2996884
TakeLastViaArrayCopy took 00:00:00.2906820
TakeLastViaBlockCopy took 00:00:00.2985933
Array.Copy()
更简单,因此可以使用它。
从C#8 开始,您可以像这样使用Range 。
var lastN = array[^n..];
myArray.Reverse().Take(toTake).Reverse();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.