Толик Панков
hex_laden
............ .................. ................
October 2025
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

Толик Панков [userpic]
С#, Заполнение массива одним значением.

Преамбула


Практического значения эта задача обычно не имеет (на самом деле применение есть, покажу позже), но, тем не менее, иногда ее дают в качестве учебной задачи. Попробуем ее решить на C#.

Примечание: Создаваемый по умолчанию массив, автоматически заполняется нолями (0x00).
А нам, как раз, наоборот, надо создать массив с паттерном 0xFF или 0xAA.

Решение "в лоб"


Я думаю, оно всем понятно. Создать массив и заполнить его нужными значениями:

int items = 536870912;
byte[] tArr = new byte[items];

for (int i = 0; i < items; i++)
{
    tArr[i] = 0xFF;
}


Тут мы в цикле присваиваем каждому элементу массива значение 0xFF.

- Оно медленное.
+ Оно простое и с использованием безопасного кода.

В лоб + Array.Copy


Некто попытался оптимизировать прошлое решение, используя Array.Copy(), думая, что эта функция быстрее работает с памятью.

Что решение "в лоб", что "Array.Copy()" с распополамленным циклом работают одинаково, точнее, получилось даже медленнее (в конце статьи будет итоговая таблица):

int items = 536870912;
byte[] tArr = new byte[items];
tArr[0] = 0xFF;
for (int i = 1; i <= items / 2; i *= 2)
{
    Array.Copy(tArr, 0, tArr, i, i);
    Array.Copy(tArr, 0, tArr, i, items - i);
}


P/Invoke способ с использованием внешних библиотек


Прямая работа с памятью сильно обходит .NET по скорости, так что самый оптимальный способ для такой задачи - использовать небезопасный код.



public static class FillArray
{
    [DllImport("msvcrt.dll",
    EntryPoint = "memset",
    CallingConvention = CallingConvention.Cdecl,
    SetLastError = false)]

    private static extern IntPtr MemSet(IntPtr dest, int value, int count);

    public static byte[] FillBytes(int Length, byte Value)
    {
        byte[] Arr = new byte[Length];
        GCHandle GCH = GCHandle.Alloc(Arr, GCHandleType.Pinned);
        MemSet(GCH.AddrOfPinnedObject(), (int)Value, Length);
        return Arr;
    }


В свою программу мы импортировали функцию memset из библиотеки msvcrt.dll, т.е. смогли получить контроль над памятью. И заполнили нужным числом массив. Поскольку, за нас это делали библиотеки C++ и функции ОС, все оказалось быстро.

Сравнительная таблица скорости работы разных алгоритмов заполнения массива (массив 500 Мб).

For00:00:04.4212528
For+Array.Copy()00:00:04.6952685
MemSet (msvcrt.dll)00:00:00.2360135
Random bytes (RNGCryptoServiceProvider)00:00:06.1143497


Сравнение алгоритмов в источнике.



Тестовый пример (для этого и предыдущего [копия] поста) на GitHub

Источник

Это репост с сайта http://tolik-punkoff.com
Оригинал: http://tolik-punkoff.com/2021/01/13/s-zapolnenie-massiva-odnim-znacheniem/

Tags: ,