배열 복사 대 버퍼 블록 복사
Array.Copy 및 Buffer.BlockCopy는 모두 동일한 작업을 수행하지만 BlockCopy
빠른 바이트 수준 기본 배열 복사를 목표로 하지만 Copy
범용 구현입니다. 내 질문은-어떤 상황에서 사용해야 BlockCopy
합니까? 프리미티브 유형 배열을 복사 할 때 언제든지 사용해야합니까, 아니면 성능을 위해 코딩하는 경우에만 사용해야합니까? Buffer.BlockCopy
over 를 사용 하는 데 본질적으로 위험한 것이 Array.Copy
있습니까?
매개 변수 Buffer.BlockCopy
는 색인이 아닌 바이트 기반이므로을 사용하는 것보다 코드를 망칠 가능성이 높으므로 코드 의 성능이 중요한 섹션 Array.Copy
에서만 사용 Buffer.BlockCopy
합니다.
전주곡
나는 파티에 늦게 참여하고 있지만 32k 뷰로 이것을 올바르게 얻을 가치가 있습니다. 게시 된 답변의 대부분의 마이크로 벤치 마킹 코드는 테스트 루프에서 메모리 할당을 이동하지 않고 (심각한 GC 아티팩트를 유발하는) 테스트 변수가 아닌 결정적 실행 흐름, JIT 예열, 테스트 내 변동성을 추적하지 않습니다. 또한 대부분의 답변은 다양한 버퍼 크기와 다양한 기본 유형 (32 비트 또는 64 비트 시스템)의 영향을 테스트하지 않았습니다. 이 질문을보다 포괄적으로 해결하기 위해 필자는 개발 한 맞춤형 마이크로 벤치마킹 프레임 워크에 연결하여 일반적인 "gotchas"를 가능한 한 줄였습니다. 테스트는 32 비트 시스템과 64 비트 시스템 모두에서 .NET 4.0 릴리스 모드로 실행되었습니다. 결과는 평균 20 회 이상의 테스트 실행으로 각 실행마다 방법 당 백만 번의 테스트가 수행되었습니다. 테스트 된 기본 유형은byte
(1 바이트), int
(4 바이트) 및 double
(8 바이트). 세 가지 방법이 테스트되었습니다 : Array.Copy()
, Buffer.BlockCopy()
및 루프에서 간단한 인덱스 별 할당. 데이터가 여기에 게시하기에는 너무 방대하므로 중요한 사항을 요약하겠습니다.
테이크 아웃
- 당신의 버퍼 길이가 75 ~ 100 이하에 대한 경우, 명시 적 루프 복사 루틴은 더 빨리 (약 5 % 정도) 중 하나 이상의 보통
Array.Copy()
또는Buffer.BlockCopy()
32 비트 및 64 비트 시스템에서 테스트 3 개 원시 유형. 또한 명시 적 루프 복사 루틴은 두 가지 대안에 비해 성능의 변동성이 현저히 낮습니다. 좋은 성능은 거의 확실하게 메소드 호출 오버 헤드없이 CPU L1 / L2 / L3 메모리 캐싱에 의해 활용되는 참조의 지역성 때문 입니다.- 들어
double
버퍼 32 비트 시스템에서만 : 명시 적 루프 복사 루틴은 모든 버퍼 모두 대안이 100,000까지 시험 지정 크기보다 낫다. 다른 방법보다 3-5 % 향상되었습니다. 의 성능 때문입니다Array.Copy()
과는Buffer.BlockCopy()
완전히 네이티브 32 비트 폭을 통과에 따라 저하된다. 따라서 동일한 효과가long
버퍼 에도 적용된다고 가정합니다 .
- 들어
- 버퍼 크기가 ~ 100을 초과하는 경우 명시 적 루프 복사는 다른 두 가지 방법보다 훨씬 느리게 진행됩니다 (방금 언급 한 특정 예외 제외). 차이는 대부분 눈에 띄는
byte[]
명시 적 루프 복사가 7 배 지거나 큰 버퍼 크기를 더 느리게 수있는 곳. - 일반적으로, 3 종류의 프리미티브에 대해 시험 및 모든 버퍼 크기에 걸쳐,
Array.Copy()
와Buffer.BlockCopy()
거의 동일하게 수행 하였다. 평균적으로, 때로는Array.Copy()
2 배나 덜 걸리는 가장자리 (약 0.2 %-0.5 %가 일반적 임)의 매우 작은 가장자리를 갖는 것 같습니다Buffer.BlockCopy()
. 알 수없는 이유로,Buffer.BlockCopy()
보다 테스트 내 변동성이 눈에 띄게 높습니다Array.Copy()
. 여러 완화 조치를 시도했지만 그 이유에 대한 이론이 없는데도이 효과를 제거 할 수 없습니다. Array.Copy()
"더 똑똑하고"더 일반적이며 훨씬 더 안전한 방법 이기 때문에 평균이 약간 더 빠르며 변동성이 적을뿐 아니라Buffer.BlockCopy()
거의 모든 일반적인 경우 보다 선호됩니다 .Buffer.BlockCopy()
켄 스미스의 답변에서 지적했듯이 소스 및 대상 배열 값 유형이 다른 경우 가 훨씬 낫습니다. 이 시나리오는 일반적이지 않지만Array.Copy()
의 직접 캐스팅과 비교하여 지속적인 "안전한"값 유형 캐스팅으로 인해 성능이 매우 저하 될 수 있습니다Buffer.BlockCopy()
.- 동일한 유형의 어레이 복사
Array.Copy()
보다 빠른 StackOverflow 외부의 추가 증거는 여기Buffer.BlockCopy()
에서 찾을 수 있습니다 .
사용하는 것이 합리적 일 때의 또 다른 예는 Buffer.BlockCopy()
프리미티브 배열 (예 : 반바지)이 제공되고이를 바이트 배열 (예 : 네트워크를 통한 전송)로 변환해야하는 경우입니다. Silverlight AudioSink의 오디오를 처리 할 때이 방법을 자주 사용합니다. 샘플을 short[]
배열 로 제공 하지만 byte[]
제출하는 패킷을 작성할 때 샘플을 배열로 변환 해야합니다 Socket.SendAsync()
. 을 사용 BitConverter
하고 배열을 하나씩 반복 할 수 있지만이 작업을 수행하는 것이 훨씬 빠릅니다 (테스트에서 약 20 배).
Buffer.BlockCopy(shortSamples, 0, packetBytes, 0, shortSamples.Length * sizeof(short)).
같은 트릭도 반대로 작동합니다.
Buffer.BlockCopy(packetBytes, readPosition, shortSamples, 0, payloadLength);
이것은 안전한 C # (void *)
에서 C 및 C ++에서 일반적으로 사용되는 메모리 관리 와 거의 비슷합니다 .
필자의 테스트에 따르면 성능이 Array.Copy보다 Buffer.BlockCopy를 선호하는 이유 는 아닙니다 . 내 테스트에서 Array.Copy는 실제로 Buffer.BlockCopy보다 빠릅니다 .
var buffer = File.ReadAllBytes(...);
var length = buffer.Length;
var copy = new byte[length];
var stopwatch = new Stopwatch();
TimeSpan blockCopyTotal = TimeSpan.Zero, arrayCopyTotal = TimeSpan.Zero;
const int times = 20;
for (int i = 0; i < times; ++i)
{
stopwatch.Start();
Buffer.BlockCopy(buffer, 0, copy, 0, length);
stopwatch.Stop();
blockCopyTotal += stopwatch.Elapsed;
stopwatch.Reset();
stopwatch.Start();
Array.Copy(buffer, 0, copy, 0, length);
stopwatch.Stop();
arrayCopyTotal += stopwatch.Elapsed;
stopwatch.Reset();
}
Console.WriteLine("bufferLength: {0}", length);
Console.WriteLine("BlockCopy: {0}", blockCopyTotal);
Console.WriteLine("ArrayCopy: {0}", arrayCopyTotal);
Console.WriteLine("BlockCopy (average): {0}", TimeSpan.FromMilliseconds(blockCopyTotal.TotalMilliseconds / times));
Console.WriteLine("ArrayCopy (average): {0}", TimeSpan.FromMilliseconds(arrayCopyTotal.TotalMilliseconds / times));
출력 예 :
bufferLength: 396011520
BlockCopy: 00:00:02.0441855
ArrayCopy: 00:00:01.8876299
BlockCopy (average): 00:00:00.1020000
ArrayCopy (average): 00:00:00.0940000
ArrayCopy는 BlockCopy보다 똑똑합니다. 소스와 대상이 동일한 배열 인 경우 요소를 복사하는 방법을 알아냅니다.
int 배열을 0,1,2,3,4로 채우고 다음을 적용하면 :
Array.Copy (배열, 0, 배열, 1, 배열 길이-1);
예상대로 0,0,1,2,3으로 끝납니다.
이것을 BlockCopy로 시도하면 0,0,2,3,4가됩니다. array[0]=-1
그 후에 할당하면 예상대로 -1,0,2,3,4가되지만 배열 길이가 6과 같이 짝수이면 -1,256,2,3,4,5가됩니다. 위험한 물건. 한 바이트 배열을 다른 바이트 배열로 복사하는 것 외에는 BlockCopy를 사용하지 마십시오.
There is another case where you can only use Array.Copy: if the array size is longer than 2^31. Array.Copy has an overload with a long
size parameter. BlockCopy does not have that.
To weigh in on this argument, if one is not careful how they author this benchmark they could be easily misled. I wrote a very simple test to illustrate this. In my test below if I swap the order of my tests between starting Buffer.BlockCopy first or Array.Copy the one that goes first is almost always the slowest (although its a close one). This means for a bunch of reasons which I wont go into simply running the tests multiple times esp one after the other will not give accurate results.
I resorted to maintaining the test as is with 1000000 tries each for an array of 1000000 sequential doubles. However in I then disregard the first 900000 cycles and average the remainder. In that case the Buffer is superior.
private static void BenchmarkArrayCopies()
{
long[] bufferRes = new long[1000000];
long[] arrayCopyRes = new long[1000000];
long[] manualCopyRes = new long[1000000];
double[] src = Enumerable.Range(0, 1000000).Select(x => (double)x).ToArray();
for (int i = 0; i < 1000000; i++)
{
bufferRes[i] = ArrayCopyTests.ArrayBufferBlockCopy(src).Ticks;
}
for (int i = 0; i < 1000000; i++)
{
arrayCopyRes[i] = ArrayCopyTests.ArrayCopy(src).Ticks;
}
for (int i = 0; i < 1000000; i++)
{
manualCopyRes[i] = ArrayCopyTests.ArrayManualCopy(src).Ticks;
}
Console.WriteLine("Loop Copy: {0}", manualCopyRes.Average());
Console.WriteLine("Array.Copy Copy: {0}", arrayCopyRes.Average());
Console.WriteLine("Buffer.BlockCopy Copy: {0}", bufferRes.Average());
//more accurate results - average last 1000
Console.WriteLine();
Console.WriteLine("----More accurate comparisons----");
Console.WriteLine("Loop Copy: {0}", manualCopyRes.Where((l, i) => i > 900000).ToList().Average());
Console.WriteLine("Array.Copy Copy: {0}", arrayCopyRes.Where((l, i) => i > 900000).ToList().Average());
Console.WriteLine("Buffer.BlockCopy Copy: {0}", bufferRes.Where((l, i) => i > 900000).ToList().Average());
Console.ReadLine();
}
public class ArrayCopyTests
{
private const int byteSize = sizeof(double);
public static TimeSpan ArrayBufferBlockCopy(double[] original)
{
Stopwatch watch = new Stopwatch();
double[] copy = new double[original.Length];
watch.Start();
Buffer.BlockCopy(original, 0 * byteSize, copy, 0 * byteSize, original.Length * byteSize);
watch.Stop();
return watch.Elapsed;
}
public static TimeSpan ArrayCopy(double[] original)
{
Stopwatch watch = new Stopwatch();
double[] copy = new double[original.Length];
watch.Start();
Array.Copy(original, 0, copy, 0, original.Length);
watch.Stop();
return watch.Elapsed;
}
public static TimeSpan ArrayManualCopy(double[] original)
{
Stopwatch watch = new Stopwatch();
double[] copy = new double[original.Length];
watch.Start();
for (int i = 0; i < original.Length; i++)
{
copy[i] = original[i];
}
watch.Stop();
return watch.Elapsed;
}
}
https://github.com/chivandikwa/Random-Benchmarks
Just want to add my testing case which shows again BlockCopy has no 'PERFORMANCE' benefit over Array.Copy. They seem to have the same performance under release mode on my machine (both take about 66ms to copy 50 million integers). Under debug mode, BlockCopy is just marginally faster.
private static T[] CopyArray<T>(T[] a) where T:struct
{
T[] res = new T[a.Length];
int size = Marshal.SizeOf(typeof(T));
DateTime time1 = DateTime.Now;
Buffer.BlockCopy(a,0,res,0, size*a.Length);
Console.WriteLine("Using Buffer blockcopy: {0}", (DateTime.Now - time1).Milliseconds);
return res;
}
static void Main(string[] args)
{
int simulation_number = 50000000;
int[] testarray1 = new int[simulation_number];
int begin = 0;
Random r = new Random();
while (begin != simulation_number)
{
testarray1[begin++] = r.Next(0, 10000);
}
var copiedarray = CopyArray(testarray1);
var testarray2 = new int[testarray1.Length];
DateTime time2 = DateTime.Now;
Array.Copy(testarray1, testarray2, testarray1.Length);
Console.WriteLine("Using Array.Copy(): {0}", (DateTime.Now - time2).Milliseconds);
}
참고URL : https://stackoverflow.com/questions/1389821/array-copy-vs-buffer-blockcopy
'Programing' 카테고리의 다른 글
치명적인 오류 인 이유 : 클래스 'PHPUnit_Framework_TestCase'를…에서 찾을 수 없습니까? (0) | 2020.07.19 |
---|---|
Android에서 사용자 정의 모양에 그림자 추가 (0) | 2020.07.19 |
SQLite에 Long 유형이 있습니까? (0) | 2020.07.18 |
Django REST Framework : ModelSerializer에 추가 필드 추가 (0) | 2020.07.18 |
XAML에 여러 값 변환기를 연결하는 방법이 있습니까? (0) | 2020.07.18 |