Array.Copy i Buffer.BlockCopy robią to samo, ale BlockCopy
mają na celu szybkie kopiowanie tablic pierwotnych na poziomie bajtów, podczas gdy Copy
jest to implementacja ogólnego przeznaczenia. Moje pytanie brzmi - w jakich okolicznościach należy używać BlockCopy
? Czy należy go używać w dowolnym momencie podczas kopiowania tablic typów pierwotnych, czy też należy go używać tylko wtedy, gdy kodujesz pod kątem wydajności? Czy jest coś z natury niebezpiecznego w używaniu Buffer.BlockCopy
więcej Array.Copy
?
124
Marshal.Copy
:-). Cóż, użyjArray.Copy
dla typów referencyjnych, złożonych typów wartości i jeśli typ się nie zmienia,Buffer.BlockCopy
do „konwersji” między typami wartości, tablicami bajtów i magią bajtów. F.ex. połączenie zStructLayout
jest dość potężne, jeśli wiesz, co robisz. Jeśli chodzi o wydajność, wydaje się, że najszybsze w tym celu wywołaniememcpy
/cpblk
jest niezarządzane - patrz code4k.blogspot.nl/2010/10/… .byte[]
. Nie było różnicy w wersji wydania. CzasamiArray.Copy
, czasamiBuffer.BlockCopy
(nieco) szybciej.Array.Copy
jest raczej wersją wyspecjalizowaną - na przykład może kopiować tylko te same tablice rangi.Odpowiedzi:
Ponieważ parametry
Buffer.BlockCopy
są oparte na bajtach, a nie na indeksach, bardziej prawdopodobne jest, że zepsujesz kod, niż gdybyś używałArray.Copy
, więc użyłbym tylkoBuffer.BlockCopy
w sekcji mojego kodu krytycznej dla wydajności.źródło
UInt16
to dwa bajty na element. Jeśli przekażesz tę tablicę do BlockCopy wraz z liczbą elementów w tablicy, oczywiście tylko połowa tablicy zostanie skopiowana. Aby to działało poprawnie, jako parametr długości należy podać liczbę elementów razy rozmiar każdego elementu (2). msdn.microsoft.com/en-us/library/ ... i wyszukajINT_SIZE
w przykładach.Preludium
Dołączam do imprezy późno, ale przy 32 tysiącach wyświetleń warto zrobić to dobrze. Większość kodu mikroznakowania w opublikowanych dotychczas odpowiedziach ma jedną lub więcej poważnych błędów technicznych, w tym brak przenoszenia alokacji pamięci poza pętle testowe (co wprowadza poważne artefakty GC), brak testowania zmiennych a deterministyczne przepływy wykonywania, rozgrzewka JIT, a nie śledzenie zmienności wewnątrztestowej. Ponadto w większości odpowiedzi nie testowano skutków różnych rozmiarów buforów i różnych typów pierwotnych (w odniesieniu do systemów 32-bitowych lub 64-bitowych). Aby bardziej kompleksowo odpowiedzieć na to pytanie, podłączyłem go do opracowanego przeze mnie niestandardowego schematu mikroznakowania, który ogranicza większość typowych „pułapek” w możliwie największym stopniu. Testy przeprowadzono w trybie wydania platformy .NET 4.0 zarówno na komputerze 32-bitowym, jak i 64-bitowym. Wyniki uśredniono z 20 serii testowych, z których każdy miał 1 milion prób na metodę. Testowane były typy prymitywne
byte
(1 bajt),int
(4 bajty) idouble
(8 bajtów). Badano trzy sposoby:Array.Copy()
,Buffer.BlockCopy()
i proste zadanie za indeksowanie w pętli. Dane są zbyt obszerne, aby je tutaj opublikować, więc podsumuję najważniejsze punkty.Na wynos
Array.Copy()
lubBuffer.BlockCopy()
wszystkie 3 typy pierwotne testowane na komputerach 32-bitowych i 64-bitowych. Ponadto jawna procedura kopiowania w pętli ma zauważalnie mniejszą zmienność wydajności w porównaniu z dwiema alternatywami. Dobra wydajność jest prawie na pewno spowodowana lokalnością odniesienia wykorzystywaną przez buforowanie pamięci CPU L1 / L2 / L3 w połączeniu z brakiem narzutu wywołania metody.double
buforów na komputerach 32-bitowych : jawna procedura kopiowania w pętli jest lepsza niż obie alternatywy dla wszystkich testowanych rozmiarów buforów do 100 KB. Poprawa jest o 3-5% lepsza niż w przypadku innych metod. Dzieje się tak, ponieważ wydajnośćArray.Copy()
iBuffer.BlockCopy()
zostaje całkowicie obniżona po przejściu natywnej 32-bitowej szerokości. Dlatego zakładam, że ten sam efekt miałby również zastosowanie dolong
buforów.byte[]
przypadku, gdy jawne kopiowanie w pętli może stać się 7x lub wolniejsze przy dużych rozmiarach buforów.Array.Copy()
iBuffer.BlockCopy()
wykonanych prawie identycznie. ŚrednioArray.Copy()
wydaje się mieć bardzo niewielką przewagę wynoszącą około 2% lub mniej czasu potrzebnego (ale 0,2% - 0,5% lepiej jest typowe), chociażBuffer.BlockCopy()
czasami go pokonuje. Z nieznanych powodówBuffer.BlockCopy()
ma zauważalnie większą zmienność wewnątrztestową niżArray.Copy()
. Tego efektu nie udało się wyeliminować, mimo że próbowałem wielu środków zaradczych i nie miałem działającej teorii, dlaczego.Array.Copy()
jest to „mądrzejsza”, ogólniejsza i znacznie bezpieczniejsza metoda, oprócz tego, że jest bardzo nieznacznie szybsza i średnio ma mniejszą zmienność, powinna być preferowanaBuffer.BlockCopy()
w prawie wszystkich typowych przypadkach. Jedynym przypadkiem użycia, w którymBuffer.BlockCopy()
będzie znacznie lepszy, jest sytuacja, gdy typy wartości tablicy źródłowej i docelowej są różne (jak wskazano w odpowiedzi Kena Smitha). Chociaż ten scenariusz nie jest powszechny,Array.Copy()
może tutaj działać bardzo słabo ze względu na ciągłe rzutowanie typu „bezpiecznego” typu wartości w porównaniu z rzutowaniem bezpośrednimBuffer.BlockCopy()
.Array.Copy()
są szybsze niż wBuffer.BlockCopy()
przypadku kopiowania tablic tego samego typu, można znaleźć tutaj .źródło
Array.Clear()
najpierw zaczyna pokonać wyraźne rozliczeń zadanie pętli tablicy (ustawieniefalse
,0
lubnull
). Jest to zgodne z moimi podobnymi ustaleniami powyżej. Te oddzielne testy porównawcze zostały odkryte w Internecie tutaj: manski.net/2012/12/net-array-clear-vs-arrayx-0-performanceLoop Results for 1000000 iterations 17.9515ms. Buffer.BlockCopy Results for 1000000 iterations 39.8937ms. Array.Copy Results for 1000000 iterations 45.9059ms
Jeśli jednak rozmiar kopii> ~ 20 bajtów, jawna pętla jest znacznie wolniejsza.Innym przykładem sensownego użycia
Buffer.BlockCopy()
jest sytuacja, w której otrzymujesz tablicę prymitywów (powiedzmy, short) i musisz przekonwertować ją na tablicę bajtów (powiedzmy, do transmisji przez sieć). Często używam tej metody, gdy mam do czynienia z dźwiękiem z Silverlight AudioSink. Dostarcza próbkę jakoshort[]
tablicę, ale musisz ją przekonwertować nabyte[]
tablicę podczas budowania pakietu, do którego przesyłaszSocket.SendAsync()
. Możesz użyćBitConverter
i iterować po tablicy jeden po drugim, ale jest to o wiele szybsze (około 20x w moich testach), aby zrobić to:Ta sama sztuczka działa również w odwrotnej kolejności:
Jest to tak bliskie, jak w bezpiecznym C #,
(void *)
rodzaju zarządzania pamięcią, które jest tak powszechne w C i C ++.źródło
MemoryMarshal.AsBytes<T>
lubMemoryMarshal.Cast<TFrom, TTo>
pozwolić ci zinterpretować twoją sekwencję jednego prymitywu jako sekwencję innego prymitywu.Na podstawie moich testów wydajność nie jest powodem do preferowania Buffer.BlockCopy nad Array.Copy. Z mojego testu Array.Copy jest faktycznie szybszy niż Buffer.BlockCopy.
Przykładowe dane wyjściowe:
źródło
ArrayCopy jest inteligentniejszy niż BlockCopy. Dowiaduje się, jak skopiować elementy, jeśli źródło i miejsce docelowe są tą samą tablicą.
Jeśli wypełnimy tablicę int 0,1,2,3,4 i zastosujemy:
zgodnie z oczekiwaniami otrzymujemy 0,0,1,2,3.
Spróbuj tego z BlockCopy i otrzymamy: 0,0,2,3,4. Jeśli przypiszę
array[0]=-1
po tym, otrzymamy -1,0,2,3,4 zgodnie z oczekiwaniami, ale jeśli długość tablicy jest parzysta, np. 6, otrzymamy -1,256,2,3,4,5. Niebezpieczne rzeczy. Nie używaj BlockCopy inaczej niż do kopiowania jednej tablicy bajtów do drugiej.Istnieje inny przypadek, w którym możesz użyć tylko Array.Copy: jeśli rozmiar tablicy jest dłuższy niż 2 ^ 31. Array.Copy ma przeciążenie z
long
parametrem rozmiaru. BlockCopy tego nie ma.źródło
Aby rozważyć ten argument, jeśli ktoś nie jest ostrożny, w jaki sposób tworzą ten wzorzec, może łatwo zostać wprowadzony w błąd. Aby to zilustrować, napisałem bardzo prosty test. W moim teście poniżej, jeśli zamienię kolejność moich testów między uruchomieniem Buffer.BlockCopy najpierw lub Array.Copy, ten, który idzie pierwszy, jest prawie zawsze najwolniejszy (chociaż jest bliski). Oznacza to, że z wielu powodów, którym nie będę się zajmować, po prostu wielokrotne przeprowadzanie testów, szczególnie jeden po drugim, nie da dokładnych wyników.
Uciekłem się do utrzymania testu, tak jak w przypadku 1000000 prób dla tablicy 1000000 sekwencyjnych podwójnych. Jednak w tym czasie pomijam pierwsze 900000 cykli, a resztę uśredniam. W takim przypadku bufor jest lepszy.
https://github.com/chivandikwa/Random-Benchmarks
źródło
Chcę tylko dodać mój przypadek testowy, który ponownie pokazuje, że BlockCopy nie ma przewagi „PERFORMANCE” w porównaniu z Array.Copy. Wydaje się, że mają taką samą wydajność w trybie wydania na moim komputerze (kopiowanie 50 milionów liczb całkowitych trwa około 66 ms). W trybie debugowania BlockCopy jest tylko nieznacznie szybsze.
źródło