3 回答

TA貢獻(xiàn)1801條經(jīng)驗(yàn) 獲得超8個(gè)贊
使用Buffer.BlockCopy。它的全部目的是快速執(zhí)行(請(qǐng)參見(jiàn)Buffer):
與System.Array類(lèi)中的類(lèi)似方法相比,此類(lèi)提供了更好的操作原始類(lèi)型的性能。
誠(chéng)然,我還沒(méi)有做過(guò)任何基準(zhǔn)測(cè)試,但這就是文檔。它也適用于多維數(shù)組。只需確保始終指定要復(fù)制的字節(jié)數(shù),而不是指定多少個(gè)元素,并且確保您正在處理原始數(shù)組。
另外,我還沒(méi)有對(duì)此進(jìn)行測(cè)試,但是如果將委托綁定到并直接調(diào)用它,則可能會(huì)使系統(tǒng)的性能降低System.Buffer.memcpyimpl一點(diǎn)。簽名是:
internal static unsafe void memcpyimpl(byte* src, byte* dest, int len)
它確實(shí)需要指針,但是我相信它已針對(duì)可能的最高速度進(jìn)行了優(yōu)化,因此,即使您手頭有組裝,我也認(rèn)為沒(méi)有任何方法可以使速度更快。
更新:
由于要求(并滿足我的好奇心),我對(duì)此進(jìn)行了測(cè)試:
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();
}
}
}
結(jié)果:
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
現(xiàn)在更改順序:
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
現(xiàn)在再次更改順序:
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
或者換句話說(shuō):他們很有競(jìng)爭(zhēng)力;一般而言,memcpyimpl速度最快,但不必?fù)?dān)心。

TA貢獻(xiàn)1789條經(jīng)驗(yàn) 獲得超10個(gè)贊
對(duì)于基本類(lèi)型的數(shù)組(如double),即使對(duì)于帶有指針的多維數(shù)組,也可以快速?gòu)?fù)制。
在下面的代碼中,我初始化一個(gè)2D數(shù)組A[10,10],其值是1到100。然后將這些值復(fù)制到1D數(shù)組中。B[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}
}
}
}
有多快 我的測(cè)試表明,它比本機(jī)C#復(fù)制和復(fù)制要快很多倍Buffer.BlockCopy()。您可以根據(jù)自己的情況嘗試一下,并告訴我們。
編輯1 我比較了復(fù)制和四種方法。1)兩個(gè)嵌套循環(huán),2)一個(gè)串行循環(huán),3)指針,4)BlockCopy。我測(cè)量了各種尺寸陣列的每刻度的副本數(shù)。
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
很明顯,誰(shuí)是贏家。指針復(fù)制比任何其他方法好幾個(gè)數(shù)量級(jí)。
編輯2 顯然,我不公平地利用了編譯器/ JIT優(yōu)化,因?yàn)楫?dāng)我將循環(huán)移動(dòng)到代表后面以平衡競(jìng)爭(zhēng)環(huán)境時(shí),數(shù)字發(fā)生了巨大變化。
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
緩沖的副本位于此處的頂部(感謝@Mehrdad),其次是指針副本?,F(xiàn)在的問(wèn)題是,為什么指針復(fù)制不如緩沖區(qū)方法那么快?

TA貢獻(xiàn)1877條經(jīng)驗(yàn) 獲得超1個(gè)贊
如果在.NET Core上運(yùn)行,則可以考慮使用source.AsSpan().CopyTo(destination)(但請(qǐng)注意在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 |
添加回答
舉報(bào)