Сохранение 2D-массива с помощью Protobuf (C #)

#c# #arrays #list #protobuf-net

#c# #массивы #Список #protobuf-net

Вопрос:

У меня есть большой многомерный массив, который необходимо сохранить с помощью protobuf. В массиве может быть до 5120 * 5120 = 26 214 400 элементов. К сожалению, Protobuf не поддерживает хранение многомерных массивов.

В качестве теста я написал две функции и дополнительный класс. Класс хранит и x,y, которые указывают на местоположение внутри массива (array[x, y] ). Класс имеет «значение», которое представляет собой данные из массива [x, y]. Я использую список для хранения этих данных.

Когда я генерирую довольно небольшой массив (1024 * 1024), я получаю выходной файл размером более 169 МБ. Из моего тестирования он загружает и генерирует файл очень быстро, поэтому проблем нет. Однако размер файла огромен — мне определенно нужно сократить размер.

Это нормальный размер файла или мне нужно переосмыслить весь свой процесс? Должен ли я сжимать данные перед их сохранением (архивирование файла занимает от 169 МБ до 6 МБ)? Если да, то какой самый быстрый / простой способ заархивировать файл на C #?

Это псевдокод, основанный на моем реальном коде.

 [ProtoContract]
public class Example
{
    [ProtoIgnore]
    public string[,] MyArray { get; set; }

    [ProtoMember(0)]
    private List<MultiArray> Storage { get; set; }

    public void MoveToList()
    {
        for (int x = 0; x < MyArray.GetLength(0); x  )
        {
            for (int y = 0; y < MyArray.GetLength(1); y  )
            {
                Storage.Add(new MultiArray
                {
                    _x = x,
                    _y = y,
                    value = MyArray[x, y]
                }); 
            }
        }
    }

    public void MoveToArray()
    {
        MyArray = new string[1024, 1024];
        for (int i = 0; i < Storage.Count; i  )
        {
            MyArray[Storage[i].X, Storage[i].Y] = Storage[i]._value;
        }
    }
}

[ProtoContract]

public class MultiArray
{
    [ProtoMember(0)]
    public int _y { get; set; }
    [ProtoMember(1)]
    public int _x { get; set; }
    [ProtoMember(2)]
    public string _value { get; set; }
}
 

Примечания: значение должно быть правильным x / y массива.

Я ценю любые предложения.

Ответ №1:

Я не знаю о хранилище, но, вероятно, это неправильный способ сделать это.
То, как вы это делаете, вы создаете объект MultiArray для каждой ячейки вашего массива.
Более простым и эффективным решением было бы сделать это:

 String[] Storage = new String[1024*1024];
int width = 1024
int height = 1024;
for (int x = 0; x < width; x  )
{
    for (int y = 0; y < height; y  )
    {
        Storage[x*width y]=MyArray[x,y];
    }
}  
 

Комментарии:

1. Спасибо! Я поэкспериментирую с этим сегодня вечером.

Ответ №2:

В конечном счете, формат protobuf не имеет понятия о массивах более высокой размерности, чем единица.

На уровне библиотеки, поскольку вы используете protobuf-net, мы могли бы заставить библиотеку творить здесь какую-то магию, по сути, рассматривая ее как;

 message Array_T {
    repeated int32 dimensions;
    repeated T items; // packed when possible
}
 

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

Однако это было бы немного неудобно с точки зрения x-plat.

Но чтобы проверить, поможет ли это, вы могли бы линеаризовать свой 2D-массив и посмотреть, какое место он занимает.

В вашем случае я подозреваю, что реальная проблема (изменение размера) заключается в количестве строк. Protobuf записывает содержимое строки каждый раз, без каких-либо попыток в таблицах поиска. Возможно, также стоит проверить, какова общая длина строки sunlm (в байтах UTF-8) для содержимого вашего массива.