2014-02-06 89 views
4

对于一个项目,我将不得不经常使用包含大量数据的数组的N个最后一个元素。获得数组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; 
} 

这种运作良好,这是不能通用的(我希望这可以为任何基本类型(整型,浮点,双,...)工作

有没有一种方法,以获得具有通用/ LINQ/...方法相当的性能?我不需要做它的工作原理上的IEnumerable,阵列是对我来说足够。

编辑 我目前正在测试所有我thods你给我的,现在它的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 
+1

'new T [count]'后面跟着'Array.Copy'?顺便说一句,以什么方式演示'Enumerable.Skip' /'Enumerable.Take'的速度很慢的短程序会很好,这将使实际测试建议的替代品成为可能,而不是主要猜测。 – hvd

+0

是的,我会认为Array.Copy()会和指针算术一样快,因为它可能使用memcpy等效实现。 –

+0

我不认为它会产生明显的差异,但是您对'Take()'的调用是不必要的。如果您只是使用'myArray.Skip(myArray.Length - toTake)',性能是否会提高很多? – BACON

回答

5

从评论:

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。你不能复制速度比不复制。 :)

+0

+1我的测试显示这是最好的解决方案,国际海事组织。也许你应该在开始时添加'count = Math.Min(count,inputArray.Length);'。 –

+0

谢谢你,我的所有测试结果都是更好的解决方案。你将如何避免新阵列? (在我的情况下,我可以直接引用基本数组,无论原始目标还是目标都将被更改,只读操作) – J4N

+0

@ J4N您可以创建一个自定义的'IEnumerable ',它拥有对原始数组的引用,和该数组的索引。这与Enumerable.Skip的工作原理类似,但首先,由于您知道您正在处理数组,因此您可以通过索引访问数组元素,其次,您有足够的额外信息来避免不必要的装箱操作。是否比复制数据更好取决于所涉及的数据。 – hvd

0
myArray.Reverse().Take(toTake).Reverse(); 
+0

似乎比Skip.Take快了一点,但仍需要很多时间 – J4N

3

这个怎么样?应该是相当快:

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()比较简单,所以这是使用一个。

+0

不错,但为什么这会比Array.Copy更快?现在我的测试没有显示出更快。 – J4N

+0

@ J4N这不是更快 - 我只是试了一下。 Array.Copy()更快。我会在我的答案中提供免责声明 - 您需要发布您的Array.Copy()答案。 :) –

+0

是的,但我想奖励此解决方案附带的hvc。所以如果他不发布我会的。 – J4N

相关问题