Разрезать список на пакеты в зависимости от размера байта - PullRequest
1 голос
/ 07 апреля 2020

Не найдено похожего вопроса

Я хочу разделить массив на более мелкие партии в зависимости от размера байта. Это моя реализация, которая немного медленная. В частности, я хочу знать, есть ли встроенная функциональность, которая может мне это помочь, или, может быть, я могу улучшить свой подход ниже.

Когда я накапливал размер отдельных предметов, он не соответствовал правильно размер партии. возможно, дополнительные метаданные в самом потоке ..

private static List<List<T>> SliceLogsIntoBatches<T>(List<T> data) where T : Log
{
    const long batchSizeLimitInBytes = 1048576;

    var batches = new List<List<T>>();

    while (data.Count > 0)
    {
        var batch = new List<T>();

        batch.AddRange(data.TakeWhile((log) =>
            {
                var currentBatchSizeInBytes = GetObjectSizeInBytes(batch); // this will slow down as takewhile moves on
                return (currentBatchSizeInBytes < batchSizeLimitInBytes);
            }));

        batches.Add(batch);
        data = data.Except(batch).ToList();
    }

    return batches;
}

private static long GetObjectSizeInBytes(object objectToGetSizeFor)
{
    using (var objectAsStream = ConvertObjectToMemoryStream(objectToGetSizeFor))
    {
        return objectAsStream.Length;
    }
}

Ответы [ 2 ]

0 голосов
/ 08 апреля 2020

Я думаю, что ваш подход может быть улучшен с помощью следующей идеи: мы можем вычислить приблизительный размер пакета как сумму размеров объектов данных; и затем используйте этот приблизительный размер партии, чтобы сформировать фактическую партию; фактический размер пакета - это размер списка объектов данных. Если мы используем эту идею, мы можем уменьшить количество вызовов метода GetObjectSizeInBytes.

Вот код, который реализует эту идею:

private static List<List<T>> SliceLogsIntoBatches<T>(List<T> data) where T : Log
{
    const long batchSizeLimitInBytes = 1048576;

    var batches = new List<List<T>>();
    var currentBatch = new List<T>();

    // At first, we calculate size of each data object.
    // We will use them to calculate an approximate size of the batch.
    List<long> sizes = data.Select(GetObjectSizeInBytes).ToList();

    int index = 0;
    // Approximate size of the batch.
    long dataSize = 0;

    while (index < data.Count)
    {
        dataSize += sizes[index];

        if (dataSize <= batchSizeLimitInBytes)
        {
            currentBatch.Add(data[index]);
            index++;
        }

        // If approximate size of the current batch is greater
        // than max batch size we try to form an actual batch by:
        // 1. calculating actual batch size via GetObjectSizeInBytes method;
        // and then
        // 2. excluding excess data objects if actual batch size is greater
        //    than max batch size.
        if (dataSize > batchSizeLimitInBytes || index >= data.Count)
        {
            // This loop excludes excess data objects if actual batch size
            // is greater than max batch size.
            while (GetObjectSizeInBytes(currentBatch) > batchSizeLimitInBytes)
            {
                index--;
                currentBatch.RemoveAt(currentBatch.Count - 1);
            }

            batches.Add(currentBatch);

            currentBatch = new List<T>();
            dataSize = 0;
        }
    }

    return batches;
}

Вот полный пример , который демонстрирует этот подход.

0 голосов
/ 07 апреля 2020

Вы продолжаете пересчитывать размер создаваемой вами партии. Таким образом, вы много пересчитываете размер некоторых элементов данных. Было бы полезно, если бы вы вычислили размер данных каждого элемента данных и просто добавили его в переменную, чтобы отслеживать текущий размер пакета.

Попробуйте что-то вроде этого:

long batchSizeLimitInBytes = 1048576;
var batches = new List<List<T>>();
var currentBatch = new List<T>();
var currentBatchLength = 0;
for (int i = 0; i < data.Count; i++)
{
    var currentData = data[i];
    var currentDataLength = GetObjectSizeInBytes(currentData);
    if (currentBatchLength + currentDataLength > batchSizeLimitInBytes)
    {
        batches.Add(currentBatch);
        currentBatchLength = 0;
        currentBatch = new List<T>();
    }

    currentBatch.Add(currentData);
    currentBatchLength += currentDataLength;
}

В качестве идентификатора я, вероятно, захочу преобразовать данные в байтовые потоки только один раз, поскольку это дорогостоящая операция. В настоящее время вы конвертируете в потоки только для проверки длины, вы можете захотеть, чтобы этот метод не возвращал пакетные потоки вместо List<List<T>>.

...