Зачем загружать в лазурный blob так медленно?

У меня есть пользовательский поток, который используется для выполнения операций записи непосредственно в облаке облаков страниц.

public sealed class WindowsAzureCloudPageBlobStream : Stream
{
    // 4 MB is the top most limit for page blob write operations
    public const int MaxPageWriteCapacity = 4 * 1024 * 1024;

    // Every operation on a page blob has to manipulate a value which is rounded up to 512 bytes
    private const int PageBlobPageAdjustmentSize = 512;

    private CloudPageBlob _pageBlob;

    public override void Write(byte[] buffer, int offset, int count)
    {
        var additionalOffset = 0;
        var bytesToWriteTotal = count;

        List<Task> list = new List<Task>();
        while (bytesToWriteTotal > 0)
        {
            var bytesToWriteTotalAdjusted = RoundUpToPageBlobSize(bytesToWriteTotal);

            // Azure does not allow us to write as many bytes as we want
            // Max allowed size per write is 4MB
            var bytesToWriteNow = Math.Min((int)bytesToWriteTotalAdjusted, MaxPageWriteCapacity);
            var adjustmentBuffer = new byte[bytesToWriteNow];
            ...
            var memoryStream = new MemoryStream(adjustmentBuffer, 0, bytesToWriteNow, false, false);
            var task = _pageBlob.WritePagesAsync(memoryStream, Position, null);
            list.Add(task);
        }

        Task.WaitAll(list.ToArray());
    }

    private static long RoundUpToPageBlobSize(long size) 
    { 
        return (size + PageBlobPageAdjustmentSize - 1) & ~(PageBlobPageAdjustmentSize - 1); 
    }

У меня низкая производительность Write(). Например:

Stopwatch s = new Stopwatch();
s.Start();
using (var memoryStream = new MemoryStream(adjustmentBuffer, 0, bytesToWriteNow, false, false))
{
      _pageBlob.WritePages(memoryStream, Position);
}

s.Stop();
Console.WriteLine(s.Elapsed); => 00:00:01.52 == Average speed 2.4 MB/s

Как я могу улучшить свой алгоритм? Как использовать Parallel.ForEach для ускорения процесса?

Почему всего лишь 2,5 МБ/с, но не 60 Мбайт/сек, как на официальном сайте или http://blogs.microsoft.co.il/applisec/2012/01/05/windows-azure-benchmarks-part-2-blob-write-throughput/

Ответ 1

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

Несколько вещей, чтобы проверить:

  • Убедитесь, что ваша виртуальная машина не подкачивается (вы можете проверить удаленный рабочий стол). Например, сверхмалая виртуальная память объемом 768 МБ действительно слишком мала для любого практического использования, если вы спросите меня.
  • Установите собственные ограничения на подключение, особенно если вы используете небольшие виртуальные машины. ServicePointManager.DefaultConnectionLimit.
  • Увеличенные страницы увеличат производительность.
  • Пишите в несколько потоков (например, используйте Task/async/await, особенно если у вас есть много дел).

О, и еще одна вещь:

  • Не используйте эмулятор для такого рода вещей. Эмулятор не является хорошим представлением о реальном Лазуре, и, конечно же, о нем. тесты.

Основная причина, по которой вы получаете доступ, медленна, потому что вы делаете все синхронно. Тесты на microsoft доступ к блокам в нескольких потоках, что даст большую пропускную способность.

Теперь Azure также знает, что производительность является проблемой, поэтому они попытались смягчить проблему, поддерживая хранение с помощью локального кэширования. Что в основном происходит здесь, так это то, что они пишут локальные данные (f.ex. в файле), затем разбивают задачи на части, а затем используют несколько потоков для записи всего в хранилище blob. Библиотека хранения данных - одна из таких библиотек. Однако при их использовании вы всегда должны иметь в виду, что у них разные ограничения долговечности (например, включение "кэширования записи" на ваш локальный ПК) и может нарушить способ настройки вашей распределенной системы (если вы читаете и пишете то же самое хранилище от нескольких виртуальных машин).

Почему...

Вы попросили "почему". Чтобы понять, почему хранилище памяти медленное, вам нужно понять, как это работает. Сначала я хотел бы отметить, что эта презентация от Microsoft Azure, которая объясняет, как работает хранилище Azure.

Первое, что вы должны понимать, это то, что хранилище Azure поддерживается распределенным набором (вращающихся) дисков. Из-за ограничений долговечности и согласованности они также обеспечивают, чтобы "большинство голосов" записывалось в стабильное хранилище. Для производительности несколько уровней системы будут иметь кеши, которые будут в основном читать кеши (опять же из-за ограничений долговечности).

Теперь команда Azure не публикует все. К счастью для меня, 5 лет назад моя предыдущая компания создала подобную систему в меньших масштабах. У нас были подобные проблемы с производительностью, такие как Azure, и система была очень похожа на презентацию, которую я связал выше. Как таковой, я думаю, что могу объяснить и немного рассказать о том, где узкие места. Для ясности я буду отмечать разделы как предположения, где я думаю, что это подходит.

Если вы пишете страницу в хранилище blob, вы фактически настраиваете серию соединений TCP/IP, храните страницу в нескольких местах, и когда принимается большинство голосов, вы даете "хорошо" клиенту. Теперь в этой системе есть несколько узких мест:

  • Вам потребуется настроить серию TCP/IP-соединений по всей инфраструктуре. Настройка их будет стоить времени.
  • Конечным точкам хранилища будет необходимо выполнить поиск диска в нужном месте и выполнить операцию.
  • Георепликация, конечно, займет больше времени, чем локальная репликация.
  • [спекулировать] Мы также обнаружили, что много времени было потрачено во время фазы буферизации.

Число (1), (2) и (3) здесь достаточно хорошо известно. Число (4) здесь фактически является результатом (1) и (2). Обратите внимание: вы не можете просто бросить бесконечное количество запросов на вращающиеся диски; ну... на самом деле вы можете, но тогда система придет к остановке. Таким образом, для того, чтобы решить эту проблему, диск ищет от разных клиентов, как правило, планируются таким образом, что вы ищете только, если знаете, что можете писать все (чтобы минимизировать дорогостоящие поиски). Однако здесь есть проблема: если вы хотите повысить пропускную способность, вам нужно начать поиск, прежде чем у вас есть все данные, - и если вы не получите данные достаточно быстро, другие запросы должны ждать дольше. Здесь также лежит дилемма: вы можете либо оптимизировать для этого (иногда это может повредить пропускную способность каждого клиента и останавливать всех остальных, особенно со смешанными рабочими нагрузками) или буферизировать все, а затем искать и писать все сразу (это проще, но добавляет некоторые латентность для всех). Из-за огромного количества клиентов, с которыми работает Azure, я подозреваю, что они выбрали последний подход, который добавляет больше латентности в полный цикл записи.

Независимо от этого, большая часть времени, вероятно, будет потрачена на (1) и (2). Фактические пакеты данных и записи данных являются довольно быстрыми. Чтобы дать вам приблизительную оценку: вот некоторые часто используемые тайминги.

Итак, это оставляет нам один вопрос: зачем писать материал в нескольких потоках гораздо быстрее?

Причина этого на самом деле очень проста: если мы пишем материал в нескольких потоках, есть большая вероятность, что мы храним фактические данные на разных серверах. Это означает, что мы можем перенести наше узкое место с "ожидания поиска и ожидания установки сети" на "пропускную способность". И пока наша клиентская VM может справиться с этим, очень вероятно, что инфраструктура также сможет справиться с этим.

Ответ 2

Если вы не против разработки файла вместо потока (или, возможно, у него есть поддержка потока, и я не знаю об этом), посмотрите на библиотеку перемещения данных Azure Storage Data. Это лучшее, что я видел до сих пор.

Он относительно новый (на момент написания), но имеет очень хорошую поддержку для перемещения больших файлов в кусках и максимизации пропускной способности (я использую его для ночного копирования резервных копий SQL, размер которых превышает 1 ГБ).

https://azure.microsoft.com/en-us/blog/announcing-azure-storage-data-movement-library-0-2-0/

Использование довольно простое. Вот пример:

using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.WindowsAzure.Storage.DataMovement;


namespace BlobUploader
{
    public class Uploader
    {

        public string ConnectionString { get; set; }
        public string ContainerName { get; set; }
        public string BlobName { get; set; }

        public void UploadFile(string filePath) {

            CloudStorageAccount account = CloudStorageAccount.Parse(ConnectionString);
            CloudBlobClient blobClient = account.CreateCloudBlobClient();
            CloudBlobContainer blobContainer = blobClient.GetContainerReference(ContainerName);
            blobContainer.CreateIfNotExists();
            CloudBlockBlob destinationBlob = blobContainer.GetBlockBlobReference(BlobName);

            TransferManager.Configurations.ParallelOperations = 64;

            TransferContext context = new TransferContext();
            context.ProgressHandler = new Progress<TransferProgress>((progress) => {
                Console.WriteLine("Bytes uploaded: {0}", progress.BytesTransferred);
            });

            var task = TransferManager.UploadAsync(filePath, destinationBlob, null, context, CancellationToken.None);
            task.Wait();   
        }


    }
}

В следующем блоге предварительного просмотра есть информация о том, как это произошло и как оно приближается к вещам в целом:

https://azure.microsoft.com/en-us/blog/introducing-azure-storage-data-movement-library-preview-2/

Ответ 3

Простая и быстрая проверка: убедитесь, что ваше хранилище blob находится в том же регионе Azure, где работает ваша виртуальная машина или приложение. Одна из проблем, с которыми мы столкнулись, была наша учетная запись хранилища в другом регионе нашего приложения. Это привело к значительной задержке в процессе обработки. Мы расчесывали головы, пока не поняли, что читаем и пишем по регионам. Ошибка новичков с нашей стороны!