Эффективно записать массив int в файл - PullRequest
0 голосов
/ 07 мая 2019

У меня есть потенциально больший массив int, который я записываю в файл, используя BinaryWriter.Конечно, я могу использовать подход по умолчанию.

using (BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.Create)))
{
    writer.Write(myIntArray.Length);
    foreach (int value in myIntArray)
        writer.Write(value);
}

Но это кажется ужасно неэффективным.Я почти уверен, что массив int хранит данные непрерывно в памяти.Нет ли способа просто записать память напрямую в файл, как вы можете с массивом byte?Может быть, способ приведения (не копирования) массива int в массив byte?

Ответы [ 3 ]

2 голосов
/ 10 мая 2019

Я подумал, что было бы интересно сравнить каждый из методов, описанных выше, оригинал от @ Jonathan-Wood (TestCopyStream), предложение Span от @ Mike-Zboray (TestCopySpan) и Buffer BlockCopy от @ oleg-bondarenko ( TestCopySpanByteCopy) [да, назвать вещи сложно].

Я генерирую массивы int размером N случайных чисел, одинаковые для каждого прогона.

Вот результаты:

|               Method |     N |     Mean |     Error |    StdDev |   Median | Ratio | RatioSD | Rank |   Gen 0 | Gen 1 | Gen 2 | Allocated |
|--------------------- |------ |---------:|----------:|----------:|---------:|------:|--------:|-----:|--------:|------:|------:|----------:|
|         TestCopySpan |  1000 | 1.372 ms | 0.0382 ms | 0.1109 ms | 1.348 ms |  1.00 |    0.11 |    1 |       - |     - |     - |    4984 B |
|       TestCopyStream |  1000 | 1.377 ms | 0.0324 ms | 0.0935 ms | 1.364 ms |  1.00 |    0.00 |    1 |       - |     - |     - |    4984 B |
| TestCopySpanByteCopy |  1000 | 2.215 ms | 0.0700 ms | 0.2008 ms | 2.111 ms |  1.62 |    0.19 |    2 |  3.9063 |     - |     - |   13424 B |
|                      |       |          |           |           |          |       |         |      |         |       |       |           |
|         TestCopySpan | 10000 | 1.617 ms | 0.1167 ms | 0.3155 ms | 1.547 ms |  0.80 |    0.19 |    1 |       - |     - |     - |     864 B |
|       TestCopyStream | 10000 | 2.032 ms | 0.0776 ms | 0.2251 ms | 1.967 ms |  1.00 |    0.00 |    2 |       - |     - |     - |    4984 B |
| TestCopySpanByteCopy | 10000 | 2.433 ms | 0.0703 ms | 0.2040 ms | 2.430 ms |  1.21 |    0.18 |    3 | 11.7188 |     - |     - |   45304 B |
1 голос
/ 07 мая 2019

Существует поддержка наиболее эффективной формы без копирования в .NET Core через MemoryMarshal.Cast и Span<T>.Это напрямую интерпретирует память, но это потенциально непереносимо на разных платформах, поэтому ее следует использовать осторожно:

 int[] values = { 1, 2, 3 };

 using (var writer = new BinaryWriter(File.Open(path, FileMode.Create)))
 {
     Span<byte> bytes = MemoryMarshal.Cast<int, byte>(values.AsSpan());
     writer.Write(bytes);
 }

Некоторое соответствующее обсуждение этого API, когда он был перемещен из MemoryExtensions.NonPortableCast

Однако я скажу, что ваш оригинал действительно будет довольно эффективным, потому что и BinaryWriter, и FileStream имеют свои собственные внутренние буферы, которые используются при записи таких типов.

0 голосов
/ 07 мая 2019

Я не уверен, но вы можете попробовать myIntArray.SelectMany(BitConverter.GetBytes).ToArray() (время выполнения 4700мс)

есть другой подход

var binFormatter = new BinaryFormatter();
var mStream = new MemoryStream();
binFormatter.Serialize(mStream, myIntArray);  

mStream.ToArray(); 

(время выполнения 2700 мс)

Это самый быстрый подход, который я нашел и проверил с помощью средства профилирования наблюдателя: время выполнения 1500 мс (без сторонних компонентов), другие способы - около 1700 мс (MemoryMarshal), метод «для каждого» - 2700 мс

            int maxValue = Int32.MaxValue / 50;
            int[] myIntArray = Enumerable.Range(0, maxValue).ToArray();
            var path = "e:\\temp\\1.test";
            using (BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.Create)))
            {
                int intLength = myIntArray.Length;
                writer.Write(intLength);

                byte[] bytes = new byte[intLength * sizeof(int)];
                Buffer.BlockCopy(myIntArray, 0, bytes, 0, sizeof(byte));
                writer.Write(bytes);
            }
...