Helpex - Trao đổi & giúp đỡ Đăng nhập
34

Tôi có ba mảng cần được kết hợp trong một mảng ba chiều. Đoạn mã sau cho thấy hiệu suất chậm trong Trình khám phá hiệu suất. Có giải pháp nào nhanh hơn không?

for (int i = 0; i < sortedIndex.Length; i++) {
    if (i < num_in_left)
    {    
        // add instance to the left child
        leftnode[i, 0] = sortedIndex[i];
        leftnode[i, 1] = sortedInstances[i];
        leftnode[i, 2] = sortedLabels[i];
    }
    else
    { 
        // add instance to the right child
        rightnode[i-num_in_left, 0] = sortedIndex[i];
        rightnode[i-num_in_left, 1] = sortedInstances[i];
        rightnode[i-num_in_left, 2] = sortedLabels[i];
    }                    
}

Cập nhật:

Tôi thực sự đang cố gắng làm những điều sau:

//given three 1d arrays
double[] sortedIndex, sortedInstances, sortedLabels;
// copy them over to a 3d array (forget about the rightnode for now)
double[] leftnode = new double[sortedIndex.Length, 3];
// some magic happens here so that
leftnode = {sortedIndex, sortedInstances, sortedLabels};
34 hữu ích 1 bình luận 50k xem chia sẻ
74

Sử dụng Buffer.BlockCopy . Toàn bộ mục đích của nó là hoạt động nhanh (xem Bộ đệm ):

Lớp này cung cấp hiệu suất tốt hơn để thao tác các kiểu nguyên thủy so với các phương thức tương tự trong lớp System.Array.

Phải thừa nhận rằng tôi chưa thực hiện bất kỳ điểm chuẩn nào, nhưng đó là tài liệu. Nó cũng hoạt động trên các mảng đa chiều; chỉ cần đảm bảo rằng bạn luôn chỉ định bao nhiêu byte để sao chép, không phải bao nhiêu phần tử và bạn cũng đang làm việc trên một mảng nguyên thủy.

Ngoài ra, tôi chưa thử nghiệm điều này, nhưng bạn thể khai thác thêm một chút hiệu suất của hệ thống nếu bạn ràng buộc một người được ủy quyền System.Buffer.memcpyimplvà gọi trực tiếp người đó. Chữ ký là:

internal static unsafe void memcpyimpl(byte* src, byte* dest, int len)

Nó yêu cầu con trỏ, nhưng tôi tin rằng nó được tối ưu hóa cho tốc độ cao nhất có thể, và vì vậy tôi không nghĩ có cách nào để nhanh hơn thế, ngay cả khi bạn đã lắp ráp trong tay.


Cập nhật :

Do yêu cầu (và để thỏa mãn sự tò mò của tôi), tôi đã thử nghiệm điều này:

using System;
using System.Diagnostics;
using System.Reflection;

unsafe delegate void MemCpyImpl(byte* src, byte* dest, int len);

static class Temp
{
    //There really should be a generic CreateDelegate<T>() method... -___-
    static MemCpyImpl memcpyimpl = (MemCpyImpl)Delegate.CreateDelegate(
        typeof(MemCpyImpl), typeof(Buffer).GetMethod("memcpyimpl",
            BindingFlags.Static | BindingFlags.NonPublic));
    const int COUNT = 32, SIZE = 32 << 20;

    //Use different buffers to help avoid CPU cache effects
    static byte[]
        aSource = new byte[SIZE], aTarget = new byte[SIZE],
        bSource = new byte[SIZE], bTarget = new byte[SIZE],
        cSource = new byte[SIZE], cTarget = new byte[SIZE];


    static unsafe void TestUnsafe()
    {
        Stopwatch sw = Stopwatch.StartNew();
        fixed (byte* pSrc = aSource)
        fixed (byte* pDest = aTarget)
            for (int i = 0; i < COUNT; i++)
                memcpyimpl(pSrc, pDest, SIZE);
        sw.Stop();
        Console.WriteLine("Buffer.memcpyimpl: {0:N0} ticks", sw.ElapsedTicks);
    }

    static void TestBlockCopy()
    {
        Stopwatch sw = Stopwatch.StartNew();
        sw.Start();
        for (int i = 0; i < COUNT; i++)
            Buffer.BlockCopy(bSource, 0, bTarget, 0, SIZE);
        sw.Stop();
        Console.WriteLine("Buffer.BlockCopy: {0:N0} ticks",
            sw.ElapsedTicks);
    }

    static void TestArrayCopy()
    {
        Stopwatch sw = Stopwatch.StartNew();
        sw.Start();
        for (int i = 0; i < COUNT; i++)
            Array.Copy(cSource, 0, cTarget, 0, SIZE);
        sw.Stop();
        Console.WriteLine("Array.Copy: {0:N0} ticks", sw.ElapsedTicks);
    }

    static void Main(string[] args)
    {
        for (int i = 0; i < 10; i++)
        {
            TestArrayCopy();
            TestBlockCopy();
            TestUnsafe();
            Console.WriteLine();
        }
    }
}

Kết quả:

Buffer.BlockCopy: 469,151 ticks
Array.Copy: 469,972 ticks
Buffer.memcpyimpl: 496,541 ticks

Buffer.BlockCopy: 421,011 ticks
Array.Copy: 430,694 ticks
Buffer.memcpyimpl: 410,933 ticks

Buffer.BlockCopy: 425,112 ticks
Array.Copy: 420,839 ticks
Buffer.memcpyimpl: 411,520 ticks

Buffer.BlockCopy: 424,329 ticks
Array.Copy: 420,288 ticks
Buffer.memcpyimpl: 405,598 ticks

Buffer.BlockCopy: 422,410 ticks
Array.Copy: 427,826 ticks
Buffer.memcpyimpl: 414,394 ticks

Bây giờ hãy thay đổi thứ tự:

Array.Copy: 419,750 ticks
Buffer.memcpyimpl: 408,919 ticks
Buffer.BlockCopy: 419,774 ticks

Array.Copy: 430,529 ticks
Buffer.memcpyimpl: 412,148 ticks
Buffer.BlockCopy: 424,900 ticks

Array.Copy: 424,706 ticks
Buffer.memcpyimpl: 427,861 ticks
Buffer.BlockCopy: 421,929 ticks

Array.Copy: 420,556 ticks
Buffer.memcpyimpl: 421,541 ticks
Buffer.BlockCopy: 436,430 ticks

Array.Copy: 435,297 ticks
Buffer.memcpyimpl: 432,505 ticks
Buffer.BlockCopy: 441,493 ticks

Bây giờ hãy thay đổi lại thứ tự:

Buffer.memcpyimpl: 430,874 ticks
Buffer.BlockCopy: 429,730 ticks
Array.Copy: 432,746 ticks

Buffer.memcpyimpl: 415,943 ticks
Buffer.BlockCopy: 423,809 ticks
Array.Copy: 428,703 ticks

Buffer.memcpyimpl: 421,270 ticks
Buffer.BlockCopy: 428,262 ticks
Array.Copy: 434,940 ticks

Buffer.memcpyimpl: 423,506 ticks
Buffer.BlockCopy: 427,220 ticks
Array.Copy: 431,606 ticks

Buffer.memcpyimpl: 422,900 ticks
Buffer.BlockCopy: 439,280 ticks
Array.Copy: 432,649 ticks

hay nói cách khác: chúng rất cạnh tranh; theo quy tắc chung, memcpyimpllà nhanh nhất, nhưng nó không nhất thiết phải đáng lo ngại.

74 hữu ích 5 bình luận chia sẻ
11

Bạn có thể sử dụng Array.Copy.

BIÊN TẬP

Array.Copyhoạt động đối với mảng nhiều chiều: xem chủ đề này .

11 hữu ích 1 bình luận chia sẻ
5

Nếu chạy trên .NET Core, bạn có thể cân nhắc sử dụng source.AsSpan().CopyTo(destination)(mặc dù hãy cẩn thận trên Mono).

          Method |  Job | Runtime |      Mean |     Error |    StdDev | Ratio | RatioSD |
---------------- |----- |-------- |----------:|----------:|----------:|------:|--------:|
       ArrayCopy |  Clr |     Clr |  60.08 ns | 0.8231 ns | 0.7699 ns |  1.00 |    0.00 |
        SpanCopy |  Clr |     Clr |  99.31 ns | 0.4895 ns | 0.4339 ns |  1.65 |    0.02 |
 BufferBlockCopy |  Clr |     Clr |  61.34 ns | 0.5963 ns | 0.5578 ns |  1.02 |    0.01 |
                 |      |         |           |           |           |       |         |
       ArrayCopy | Core |    Core |  63.33 ns | 0.6843 ns | 0.6066 ns |  1.00 |    0.00 |
        SpanCopy | Core |    Core |  47.41 ns | 0.5399 ns | 0.5050 ns |  0.75 |    0.01 |
 BufferBlockCopy | Core |    Core |  59.89 ns | 0.4713 ns | 0.3936 ns |  0.94 |    0.01 |
                 |      |         |           |           |           |       |         |
       ArrayCopy | Mono |    Mono | 149.82 ns | 1.6466 ns | 1.4596 ns |  1.00 |    0.00 |
        SpanCopy | Mono |    Mono | 347.87 ns | 2.0589 ns | 1.9259 ns |  2.32 |    0.02 |
 BufferBlockCopy | Mono |    Mono |  61.52 ns | 1.1691 ns | 1.0364 ns |  0.41 |    0.01 |
5 hữu ích 1 bình luận chia sẻ
4

Đối với mảng kiểu nguyên thủy (như double), bạn có thể sao chép nhanh chóng, ngay cả đối với mảng nhiều chiều có con trỏ.

Trong đoạn mã dưới đây, tôi khởi tạo mảng 2D A[10,10]với các giá trị từ 1 đến 100. Sau đó, tôi sao chép các giá trị này vào mảng 1DB[100]

unsafe class Program
{ 
    static void Main(string[] args)
    {
        double[,] A = new double[10, 10];

        for(int i = 0; i < 10; i++)
        {
            for(int j = 0; j < 10; j++)
            {
                A[i, j] = 10 * i + j + 1;
            }
        }
        // A has { { 1 ,2 .. 10}, { 11, 12 .. 20}, .. { .. 99, 100} }
        double[] B = new double[10 * 10];

        if (A.Length == B.Length)
        {
            fixed (double* pA = A, pB = B)
            {
                for(int i = 0; i < B.Length; i++)
                {
                    pB[i] = pA[i];
                }
            }
            // B has {1, 2, 3, 4 .. 100}
        }
    }
}

Nó nhanh như thế nào. Thử nghiệm của tôi đã cho thấy nó nhanh hơn nhiều lần so với bản sao C # gốc và Buffer.BlockCopy(). Bạn thử nó cho trường hợp của bạn và cho chúng tôi biết.

Chỉnh sửa 1 Tôi đã so sánh sao chép với bốn phương pháp. 1) Hai vòng lặp lồng nhau, 2) Một vòng lặp nối tiếp, 3) Con trỏ, 4) BlockCopy. Tôi đã đo số bản sao mỗi lần đánh dấu cho các mảng kích thước khác nhau.

N =   10x  10 (cpy/tck) Nested = 50,  Serial = 33, Pointer =    100, Buffer =    16
N =   20x  20 (cpy/tck) Nested = 133, Serial = 40, Pointer =    400, Buffer =   400
N =   50x  50 (cpy/tck) Nested = 104, Serial = 40, Pointer =   2500, Buffer =  2500
N =  100x 100 (cpy/tck) Nested = 61,  Serial = 41, Pointer =  10000, Buffer =  3333
N =  200x 200 (cpy/tck) Nested = 84,  Serial = 41, Pointer =  40000, Buffer =  2666
N =  500x 500 (cpy/tck) Nested = 69,  Serial = 41, Pointer = 125000, Buffer =  2840
N = 1000x1000 (cpy/tck) Nested = 33,  Serial = 45, Pointer = 142857, Buffer =  1890
N = 2000x2000 (cpy/tck) Nested = 30,  Serial = 43, Pointer = 266666, Buffer =  1826
N = 5000x5000 (cpy/tck) Nested = 21,  Serial = 42, Pointer = 735294, Buffer =  1712

Ở đây rõ ràng ai là người chiến thắng. Bản sao con trỏ là đơn đặt hàng có độ lớn tốt hơn bất kỳ phương pháp nào khác.

Chỉnh sửa 2 Rõ ràng là tôi đã lợi dụng tối ưu hóa trình biên dịch / JIT một cách không công bằng vì khi tôi di chuyển các vòng ra sau các đại biểu để cân bằng sân chơi, các con số đã thay đổi đáng kể.

N =   10x  10 (cpy/tck) Nested =  0, Serial =  0, Pointer =      0, Buffer =     0
N =   20x  20 (cpy/tck) Nested = 80, Serial = 14, Pointer =    100, Buffer =   133
N =   50x  50 (cpy/tck) Nested =147, Serial = 15, Pointer =    277, Buffer =  2500
N =  100x 100 (cpy/tck) Nested = 98, Serial = 15, Pointer =    285, Buffer =  3333
N =  200x 200 (cpy/tck) Nested =106, Serial = 15, Pointer =    272, Buffer =  3076
N =  500x 500 (cpy/tck) Nested =106, Serial = 15, Pointer =    276, Buffer =  3125
N = 1000x1000 (cpy/tck) Nested =101, Serial = 11, Pointer =    199, Buffer =  1396
N = 2000x2000 (cpy/tck) Nested =105, Serial =  9, Pointer =    186, Buffer =  1804
N = 5000x5000 (cpy/tck) Nested =102, Serial =  8, Pointer =    170, Buffer =  1673

Bản sao có bộ đệm ở trên cùng ở đây (nhờ @Mehrdad) với bản sao con trỏ thứ hai. Câu hỏi bây giờ là tại sao sao chép con trỏ không nhanh như các phương thức đệm?

4 hữu ích 3 bình luận chia sẻ
0

Nếu một mảng răng cưa của biểu mẫu sau hoạt động, thì có thể tránh được một bản sao:

double[][] leftNode = new double[3][];
leftNode[0] = sortedIndex;
leftNode[1] = sortedInstances;
leftNode[2] = sortedLabels;
0 hữu ích 0 bình luận chia sẻ
loading
Không tìm thấy câu trả lời bạn tìm kiếm? Duyệt qua các câu hỏi được gắn thẻ c# arrays copy , hoặc hỏi câu hỏi của bạn.

Có thể bạn quan tâm

loading