获得数组N最后一个元素的最有效方法

时间:2014-02-06 14:19:24

标签: c# arrays performance linq

对于一个项目,我将经常采用包含大量数据的数组的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

4 个答案:

答案 0 :(得分: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。你根本不能复制比复制更快。 :)

答案 1 :(得分: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()更简单,因此就是使用它。

答案 2 :(得分:2)

C#8 开始,您可以像这样使用 Range

var express = require('express');
var mongoose = require('mongoose');
const bodyParser = require('body-parser');

var db = mongoose.connection;
db.on('error', console.error);
db.once('open', function(){
    // CONNECTED TO MONGODB SERVER
    console.log("Connected to mongod server");
});

mongoose.connect("mongodb+srv://testing1:7894@cluster0.9hxjc.mongodb.net/data? 
   retryWrites=true&w=majority", { useNewUrlParser: true, useUnifiedTopology: 
true });

var app = express();

var test = require('./route/index');
var test2 = require('./route/signup');

app.use(bodyParser.urlencoded({extended:false}));
app.use(bodyParser.json());
app.set('views',__dirname+'/views');  // == 
app.set('views',path.join(__dirname, 'views'));
app.set('view engine','ejs');
app.engine('html',require('ejs').renderFile);

app.use('/',test);
app.use('/signup.ejs',test2);

app.listen(3000,function(){
    console.log('hello world');
});

答案 3 :(得分:0)

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