Подтвердить что ты не робот

Как Array.Copy реализован на С#?

Я попытался взглянуть на реализацию Array.Copy в С# с помощью ILSpy, но он не показал мне сама реализация.

Я написал простой тест, Array.Copy и простой цикл для копирования данных. Array.Copy был быстрее.

Как это выполняется быстрее?

Спасибо, Шей

4b9b3361

Ответ 1

Те же методы, что и для быстрой записи memcpy:

  • развертка цикла
  • передача выровненных данных в больших кусках (часто с использованием SIMD)
  • подсказки кэширования CPU (здесь также помогает SIMD)

См. также:

Ответ 2

Разборка класса Array приведет вас к этому объявлению:

[MethodImpl(MethodImplOptions.InternalCall), SecurityCritical, ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
internal static extern void Copy(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, bool reliable);

Атрибут [MethodImpl] сообщает компилятору JIT, что метод фактически реализован в CLR, написанном на С++ вместо управляемого языка. Он просматривает таблицу имен методов и извлекает указатель на функцию С++, которая реализует метод и компилирует его в простую инструкцию CALL.

Получение исходного кода для CLR немного сложно, но версия SSCLI20 довольно точна для методов, которые были вокруг в течение длительного времени и не нуждались в настройке. Array.Copy(), безусловно, имеет квалификацию. Таблица, о которой я упоминал, определен в clr\src\vm\ecall.cpp, раздел, относящийся к вашему вопросу, выглядит следующим образом:

FCFuncStart(gArrayFuncs)
    FCFuncElement("Copy", SystemNative::ArrayCopy)
    FCFuncElement("Clear", SystemNative::ArrayClear)
    FCFuncElement("get_Rank", Array_Rank)
    //  etc...

Указатель функции SystemNative:: ArrayCopy() приведет вас к clr\src\vm\comsystem.cpp. Фактическая функция слишком велика, чтобы копировать здесь, не заставляя глаза заглядывать, происходит много ошибок. Он ищет способ оптимизировать копию, счастливый случай - это то, где элементы массива можно просто скопировать без преобразования. Это делается с помощью функции m_memmove(). Вы найдете эту функцию в том же файле, она используется в 32-разрядной версии CLR.

Которая сначала копирует один байт за раз, пока адрес назначения не будет выровнен на несколько из четырех байтов. Затем он копирует 16 байт за раз, 4 раза 4, эти копии бывают быстрыми, потому что они выровнены. Затем он копирует то, что осталось по одному байту за раз.

Теперь вы можете понять, почему это может быть быстрее, чем ваш собственный цикл. Он может перемещать 4 байта за раз, даже если размер элемента массива не имеет ширину 4 байта. И он может это сделать, гарантируя, что адрес копии выровнен, вы не можете, так как физический адрес элемента массива не может быть обнаружен.