2016-04-19 21 views
10

Mam niestandardowy strumień, który jest używany do wykonywania operacji zapisu bezpośrednio do blobu chmury strony.Dlaczego przesyłanie do lazuru jest tak powolne?

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); 
    } 

Mam niską wydajność Write(). Na przykład:

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 

Jak mogę poprawić mój algorytm? Jak użyć Parallel.ForEach, aby przyspieszyć proces?

Dlaczego właśnie tylko 2,5 MB/s, ale nie jest to 60MB/s jak w official site lub http://blogs.microsoft.co.il/applisec/2012/01/05/windows-azure-benchmarks-part-2-blob-write-throughput/

+0

Jeśli jeszcze nie zaznaczyłeś, możesz sprawdzić [jak skutecznie przesłać i pobrać bloby strony] (https://blogs.msdn.microsoft.com/windowsazurestorage/2010/04/10/using-windows-azure- page-blobs-and-how-to-efficient-upload-and-download-page-blobs /) –

+1

Dzięki, ale już sprawdziłeś – Anatoly

+0

Nie dostarczyłeś kompletnej implementacji (gdzie jest na przykład metoda Read, której używasz w swoim Write?). Poza tym, dlaczego mierzysz wydajność pojedynczej instrukcji (_pageBlob.WritePages), ale pytasz o słabość całego rozwiązania? – Evk

Odpowiedz

5

Podobnie jak ty, miałem dużo wydajności problemy z blokami strony - nawet jeśli nie były tak poważne. Wygląda na to, że wykonałeś pracę domową i widzę, że robisz wszystko według książki.

Kilka rzeczy do sprawdzenia:

  • Upewnij się, że maszyna wirtualna nie jest zamiana (można sprawdzić w zdalnym pulpicie). Na przykład wyjątkowo małe maszyny wirtualne z pamięcią kształtu 768 MB są naprawdę zbyt małe, aby można je było wykorzystać w praktyce, gdybyś mnie o to poprosił.
  • Ustaw własne limity połączeń, zwłaszcza jeśli używasz małych maszyn wirtualnych. ServicePointManager.DefaultConnectionLimit.
  • Większe strony zapewniają większą wydajność.
  • Napisz wiele wątków (np. Użyj Task s/async/await, zwłaszcza jeśli masz dużo pracy).

Aha i jeszcze jedno:

  • Nie używaj emulatora do tego rodzaju rzeczy. Emulator nie jest dobrą reprezentacją rzeczywistego Azure, z całą pewnością wrt. testy porównawcze.

Głównym powodem, dla którego masz czasy dostępu, jest powolne, ponieważ robisz wszystko synchronicznie. Testy porównawcze w systemie Microsoft dostęp do obiektów typu blob w wielu wątków, które dają większą przepustowość.

Azure wie również, że wydajność jest problemem, dlatego podjęli oni próbę złagodzenia problemu, wspierając pamięć masową przy użyciu lokalnego buforowania. Zasadniczo dzieje się tak, że zapisują one dane lokalne (np. W pliku), a następnie dzielą zadania na części, a następnie wykorzystują wiele wątków do zapisania wszystkiego w pamięci BLOB. Biblioteka przenoszenia danych jest jedną z takich bibliotek. Jednak podczas korzystania z nich należy zawsze pamiętać, że mają one różne ograniczenia dotyczące trwałości (to tak, jakby umożliwić "zapisywanie w pamięci podręcznej" na lokalnym komputerze) i mogą złamać sposób, w jaki zamierzałeś skonfigurować system rozproszony (jeśli czytasz & napisz to samo przechowywanie z wielu maszyn wirtualnych).

Dlaczego ...

pan poprosił o 'dlaczego'. Aby zrozumieć, dlaczego pamięć typu blob jest wolna, musisz zrozumieć, jak to działa. Najpierw chciałbym zaznaczyć, że istnieje this presentation z Microsoft Azure, który wyjaśnia, jak naprawdę działa pamięć masowa Azure.

Pierwszą rzeczą, którą należy sobie uświadomić, jest to, że pamięć masowa Azure jest wspierana przez rozproszony zestaw dysków (spinningowych). Ze względu na ograniczenia dotyczące trwałości i spójności zapewniają także "głosowanie większości", że dane są zapisywane w stabilnym magazynie. W celu uzyskania wydajności kilka poziomów systemu będzie miało pamięci podręczne, które będą głównie czytane w pamięci podręcznej (ponownie ze względu na ograniczenia dotyczące trwałości).

Teraz zespół Azure nie publikuje wszystkiego. Na szczęście dla mnie 5 lat temu moja poprzednia firma stworzyła podobny system na mniejszą skalę. Wystąpiły podobne problemy z wydajnością, jak na przykład Azure, a system był podobny do prezentacji, którą mam powyżej. W związku z tym, myślę, że mogę wyjaśnić i spekulować trochę, gdzie występują wąskie gardła. Dla jasności zaznaczę sekcje jako spekulacje, co moim zdaniem jest właściwe.

Jeśli piszesz stronę do magazynu blob, w rzeczywistości konfigurujesz serię połączeń TCP/IP, przechowujesz stronę w wielu lokalizacjach, a po otrzymaniu większości głosów dajesz "ok" z powrotem do klienta. Teraz, nie są w rzeczywistości kilka wąskich gardeł w tym systemie:

  1. Będziesz musiał skonfigurować szereg połączeń TCP/IP w całej infrastruktury. Ich ustawienie będzie kosztować czas.
  2. Punkty końcowe pamięci będą musiały wykonać dysk, aby znaleźć poprawne miejsce i wykonać operację.
  3. Geo-replikacja zajmie oczywiście więcej czasu niż replikacja lokalna.
  4. [spekulować] Stwierdziliśmy również, że dużo czasu spędziliśmy w fazie "buforowania".

Liczba (1), (2) i (3) tutaj jest dość dobrze znana. Numer (4) tutaj jest faktycznie wynikiem (1) i (2). Zauważ, że nie możesz po prostu rzucić nieskończonej liczby żądań do obracania dysków; cóż ... właściwie możesz, ale wtedy system się zatrzyma. Tak więc, aby rozwiązać ten problem, dysk szuka od różnych klientów jest zwykle zaplanowany w taki sposób, że szukasz tylko wtedy, gdy wiesz, że możesz również napisać wszystko (aby zminimalizować kosztowne poszukiwania). Istnieje jednak pewien problem: jeśli chcesz zwiększyć przepustowość, musisz zacząć szukać, zanim uzyskasz wszystkie dane - a jeśli nie otrzymujesz wystarczająco szybko danych, inne żądania muszą czekać dłużej. Tutaj także dylemat: możesz to zoptymalizować (czasami może to zranić przepustowość poszczególnych klientów i zatrzymać wszystkich innych, szczególnie przy różnych obciążeniach) lub buforować wszystko, a następnie szukać wszystkiego na raz (to jest łatwiejsze, ale dodaje trochę opóźnienie dla wszystkich). Ze względu na ogromną liczbę klientów obsługiwanych przez Azure podejrzewam, że wybrali ostatnie podejście, które dodaje więcej opóźnień do pełnego cyklu zapisu.

Niezależnie od tego, większość czasu prawdopodobnie zostanie wydana przez (1) i (2). Rzeczywiste impulsy danych i zapisy danych są dość szybkie. Aby podać przybliżoną ocenę: here are some commonly used timings.

Pozostaje nam jedno pytanie: , dlaczego pisanie artykułów w wielu wątkach jest o wiele szybsze?

Powód jest bardzo prosty: jeśli piszemy materiał w wielu wątkach, istnieje duża szansa, że ​​będziemy przechowywać rzeczywiste dane na różnych serwerach. Oznacza to, że możemy zmienić nasze wąskie gardło z "seek + network setup latency" na "throughput". Tak długo, jak może to zrobić nasza wirtualna maszyna wirtualna klienta, jest bardzo prawdopodobne, że infrastruktura również sobie z tym poradzi.

+0

Dzięki! Czy możesz wyjaśnić więcej na temat 'upewnij się, że Twoja maszyna wirtualna nie jest zamiana'? Czy muszę zmienić ustawienie mojej maszyny wirtualnej (dodać więcej pamięci), którą przekażę do magazynu blob Azure? – Anatoly

+0

@Anatoly (1) Podobnie jak normalny komputer, Twoja maszyna wirtualna przestanie działać, jeśli nie będzie miała wystarczającej ilości pamięci. XS VM mają tylko 768 MB - co wystarcza, aby uruchomić Windows - nie mówiąc już o zrobieniu czegoś. Więc tak, proste podejście polega na dodaniu większej ilości pamięci i sprawdzeniu, czy to pomaga. (2) Tak, zrobiłeś to poprawnie; Wydaje mi się, że to zauważyłem, ale najwyraźniej usunąłem go podczas sprawdzania mojej odpowiedzi. – atlaste

+0

Czy masz jakieś linki na temat praktycznego przykładu "pisać w wielu wątkach", ponieważ nie sądzę, że zmiany 'WritePages()' na 'WritePagesAsync()' mogą poprawić moją wydajność. Czy mam rację? – Anatoly

4

Jeśli nie masz nic przeciwko pracy z pliku zamiast strumienia (a może to nie ma wsparcia i strumień Nie wiem o tym), spójrz na bibliotekę przenoszenia danych Azure. To najlepsze, jakie widziałem do tej pory.

Jest stosunkowo nowy (w chwili pisania), ale ma bardzo dobre wsparcie dla przenoszenia dużych plików w porcje i maksymalizacji przepustowości (używam go do codziennego kopiowania kopii SQL, o wielkości przekraczającej 1 GB).

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

Użycie jest bardzo proste. Oto przykład:

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(); 
     } 


    } 
} 

Poniższy podgląd blogu daje pewne informacje o tym, jak do tego doszło i jak zbliża rzeczy ogólnie:

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

+0

@Anatoly - Nie testowałem tego specjalnie, ale powiedziałbym, że gdzieś tam jest. Jest dość szybki. Myślę, że mój limit jest prawdopodobnie w sieci VM, ale zgaduję co najmniej 30 MB/s, jeśli nie 40. Z pewnością znacznie lepiej niż 2,4. (daj mu szansę, to dziesięciominutowa konfiguracja i test, naprawdę) – jleach

0

Jedna prosta i szybka rzecz do sprawdzenia: upewnij się, że magazyn blobów znajduje się w tym samym regionie Azure, na którym działa Twoja maszyna wirtualna lub aplikacja. Jednym z problemów, które napotkaliśmy, było nasze konto przechowywania w innym regionie z naszej aplikacji. To spowodowało znaczne opóźnienie podczas przetwarzania. Drapieliśmy głowy, dopóki nie zorientowaliśmy się, że czytamy i piszemy w różnych regionach. Błąd w Rookie z naszej strony!

+0

Moja aplikacja na Ukrainie. Przesyłam do Europy Zachodniej – Anatoly

Powiązane problemy