top of page

Fabric Dataflow Gen2 Partitioned Compute: Nastavení a Benchmark

  • Writer: Vojtěch Šíma
    Vojtěch Šíma
  • 12 minutes ago
  • 6 min read
tl;dr Partitioned Compute v Dataflow Gen2 (in preview) je schopnost zpracovávat více souborů současně, což vede k výrazně kratší době aktualizace (refresh time). Vyžaduje to nastavení partition key a funguje pouze na podporovaných souborových systémech (ADLS Gen2). Podle mých benchmarků to docela funguje, ale pro více info si přečti článek, abys dostal celou pravdu.
Disclaimer: Obecně platí, že Dataflow Gen2 není nejrychlejší a nejlevnější Data Factory řešení v Microsoft Fabric. Pokud máš potřebné know-how nebo umíš programovat, uděláš lépe s Data pipeline aktivitou nebo non-spark Notebookem. Dataflow Gen2 je na druhou stranu extrémně uživatelsky přívětivé a má stovky vestavěných zdrojů.

Co je Partitioned Compute v Dataflow Gen2

Partitioned compute is a capability of the Dataflow Gen2 engine that allows parts of your dataflow logic to run in parallel, reducing the time to complete its evaluations.

Microsoft documentation

Kdybych to měl popsat vlastními slovy, je to schopnost enginu Dataflow Gen2 zpracovávat (podporované) soubory paralelně, tedy více souborů současně, což by mělo zkrátit dobu potřebnou pro celkovou evaluaci a tím pádem také snížit náklady (teoreticky).


Jak už jsem říkal, tato funkce je v preview a momentálně podporuje pouze soubory s odpovídajícím souborovým systémem, což je ve skutečnosti jen Azure Data Lake Storage Gen2. Takže žádný SharePoint (i když se to tak technicky můžeš pokusit nastavit, nebude to fungovat).


Stejné partitioning můžeš vytvořit i v Power BI Desktop, ale nic to neudělá, protože tato vychytývka/engine je exkluzivní pro Dataflow Gen2.

O fungování na pozadí neexistuje reálná dokumentace. Moje interpretace je spíše taková, že engine umožňuje streamovat celý proces s určitým množstvím souběžných požadavků. Část s partition, kterou vytvoříš, se váže přímo na název zpracovávaného souboru. Pokud máš správný souborový systém, umožní ti snadno přeložit úplnou cestu k danému souboru.


Při spouštění funkce AzureStorage.DataLake se nabízí několik dodatečných možností. Ty ovšem nemusí nutně zvýšit rychlost (i když při dotazování velkých souborů stojí za to si s nimi pohrát).

Nastavení Partitioned Compute v Dataflow Gen2

V Možnostech (Options) musíš nejprve vybrat dvě hlavní věci.


Přejdi do Options:

Options
Options

Poté, v Privacy, Allow combining data from multiple sources. This could expose sensitive or confidential data to an unauthorized person:

Allow combining data from multiple sources. This could expose sensitive or confidential data to an unauthorized person
Allow combining data from multiple sources. This could expose sensitive or confidential data to an unauthorized person

Pak, v Scale, Allow use of partitioned compute:

Allow use of partitioned compute
Allow use of partitioned compute

Na úrovni dotazu (Query) pak nastav svůj dotaz jako staging tak, že na něj klikneš pravým tlačítkem a vybereš Enable staging:


Enable staging
Enable staging

Jakmile máš hotovo, přichází na řadu zadání M kódu pro partition key. Pokud ke kombinování souborů používáš klikací rozhraní, proběhne generování automaticky. Pokud si chceš kód napsat sám, můžeš vložit ten od Microsoftu, ale klidně si ho uprav podle svých představ.


let
    rootPath = Text.TrimEnd(Value.Metadata(Value.Type(#"Filtered hidden files"))[FileSystemTable.RootPath]?, "\"),
    combinePaths = (path1, path2) => Text.Combine({Text.TrimEnd(path1, "\"), path2}, "\"),
    getRelativePath = (path, relativeTo) => Text.Middle(path, Text.Length(relativeTo) + 1),
    withRelativePath = Table.AddColumn(#"Filtered hidden files", "Relative Path", each getRelativePath(combinePaths([Folder Path], [Name]), rootPath), type text),
    withPartitionKey = Table.ReplacePartitionKey(withRelativePath, {"Relative Path"})
in
    withPartitionKey

V některých případech kód z Microsoft dokumentace paradoxně vytvořil neplatnou partition.

Klíčové je nastavit PartitionColumn a jeho hodnotu tak, aby napodobovala cestu za RootPath zvoleného úložiště. Nakonec se ujisti, že sloupec partition zůstane v kroku souboru tvého dotazu.


Alternativní verze může vypadat takto:

  addPartitionColumn = (tbl as table) => 
  let
    rootPath = Text.TrimEnd(Value.Metadata(Value.Type(tbl))[FileSystemTable.RootPath]?),
    getRelativePath = (root, relative) => Text.AfterDelimiter(relative, root),
    addPartitionColumn = Table.AddColumn(tbl, "PartitionColumn", each getRelativePath(rootPath, [Folder Path]) & [Name], type text),
    setPartitionColumn = Table.ReplacePartitionKey(addPartitionColumn, {"PartitionColumn"}),
    rootPathCheck = if rootPath = null then error "FileSystemTable.RootPath not found, it's either missing, or has different name. PartitionCompute may not be supported" else setPartitionColumn
  in
    rootPathCheck

Vyvoláš ji společně s transfromacemi souborů:

addTransformationForFiles = Table.AddColumn(addPartitionColumn(source), "Transform file", each fx_single_file_transformation([Content]))

Příklad Azure Delta Lake Storage Gen2 implementace:

let
  source = AzureStorage.DataLake("https://<blob_storage>.dfs.core.windows.net/"),
  
  addPartitionColumn = (tbl as table) => 
  let
    rootPath = Text.TrimEnd(Value.Metadata(Value.Type(tbl))[FileSystemTable.RootPath]?),
    getRelativePath = (root, relative) => Text.AfterDelimiter(relative, root),
    addPartitionColumn = Table.AddColumn(tbl, "PartitionColumn", each getRelativePath(rootPath, [Folder Path]) & [Name], type text),
    setPartitionColumn = Table.ReplacePartitionKey(addPartitionColumn, {"PartitionColumn"}),
    rootPathCheck = if rootPath = null then error "FileSystemTable.RootPath not found, it's either missing, or has different name. PartitionCompute may not be supported" else setPartitionColumn
  in
    rootPathCheck,
  
  addTransformationForFiles = Table.AddColumn(addPartitionColumn(source), "Transform file", each fx_single_file_transformation([Content])),
  selectColumns = Table.SelectColumns(addTransformationForFiles, {"PartitionColumn", "Transform file"}),
  expandFiles = Table.ExpandTableColumn(selectColumns, "Transform file", {"friendlyId", "somethingFriendly", "anotherFriendly", "friendlyDate"})
in
  expandFiles


Jakmile se Dataflow se správným nastavením spustí, přejdi do Recent Runs a zkontroluj detaily jednotlivých běhů. Tím si zkontroluješ, jaký engine byl skutečně použit. Pokud vidíš Engine: PartitionedCompute, dej si jedničku. Je to správně.


Engine: PartitionedCompute
Engine: PartitionedCompute

Pokud jsi nevypnul možnost FastCopy (ve výchozím nastavení je zapnutá), tvůj dotaz může místo toho sáhnout po tomto enginu. Přináší to dramaticky vyšší rychlost, což je jedině dobře. Tento článek se na FastCopy sice přímo nezaměřuje, ale pokud máš podporovaný zdroj dat (jako Lakehouse nebo ADLS2) a neprovádíš téměř žádné další transformace, může to tvou evaluaci výrazně popohnat.


Benchmark pro Partitioned Compute v Dataflow Gen2

Nyní se podívejme, zda získáme nějakou extra rychlost evaluace. Jak zaznělo v dřívějším upozornění, existuje spousta lepších možností, jak soubory zpracovat rychleji nebo levněji. Pokud ale z jakéhokoliv důvodu musíš využít Dataflow Gen2, tyhle tipy ti pomohou z něj vymáčknout maximum.


Provedl jsem různé testy a programově shromáždil časy aktualizací i spotřebované jednotky (consumption units) spojené s každým refreshem. Všechny benchmarky proběhly v jednom workspace na Fabric Trial, což by mělo víceméně odpovídat kapacitě F64.


Upozorňuji, že přesnost Average CU/sec se může lišit o nějaká ta desetinná místa.

Dále pamatuj, že uživatelské rozhraní Recent Runs ukazuje mírně odlišné časy začátku a konce pro každý refresh (u těchto běhů odchylka dělala někdy až 30 sekund), proto hodnoty ber s mírnou rezervou.

Notebook

Nejefektivnější a nejlevnější cestou jednoznačně zůstává nativní Python (bez PySparku). S použitím základního kódu zabralo zpracování 401 malých CSV souborů a jejich zápis jako delta tabulky pouhou minutu. Vzhledem k tomu, že jedna sekunda běhu nativního Python notebooku stojí jednu consumption unit, počty jsou celkem jednoduché.


Chci jen poukázat na fakt, kolik přeplácíš za jednoduché (volitelně) klikací prostředí v Dataflow Gen2.


Item Type

Avg Duration (min)

Min Duration (min)

Max Duration (min)

Average CU (s)

Average CU/sec

Notebook

1.0

0.9

1.3

60.1

0.98


Pokud by tvá pipeline vyžadovala konzumaci delta tabulky přes Semantic Model vytvořený v Power BI Desktop, přidal jsem do původních dat i časy refreshe a spotřebu u Semantic Modelu.

Item Type

Avg Duration (min)

Min Duration (min)

Max Duration (min)

Average CU (s)

Average CU/sec

Semantic Model

1.2

1.2

1.3

2000.0

27.03


Operace samotného Lakehouse na pozadí během aktualizací a interakcí se z hlediska CU drží hodně nízko, zhruba na 40 CU během refreshe sémantického modelu.

Legenda:

  • Part. Comp. - Možnost Partitioned Compute zapnuta/vypnuta

  • Modern Engine - Možnost nového moderního evaluačního enginu zapnuta/vypnuta

  • Dur. - Délka refreshe v minutách

  • CU - Capacity Unit (Consumption Unit)

  • Semantic Model - Stejný dotaz zkopírovaný do PBI Desktop, publikovaný a následně měřený refresh



Benchmark settings

Files storage: Fabric Lakehouse Files

File type: csv

File size: 2MB

File amount: 401

File rows: Každý soubor má 58k řádků, celkem 23 milionů.


Tuto variantu jsem zvolil záměrně. Zpracování velkého množství malých souborů nejlépe ukáže rozdíly v rychlosti a probíhajících optimalizacích, zatímco u menšího množství větších souborů by pravděpodobně žádný rozdíl nenastal (což je mimochodem také důležitý poznatek).


Item Type

Avg Dur. (min)

Min Dur. (min)

Max Dur. (min)

Average CU (s)

Average CU/sec

Part.

Comp.

Modern Engine

SemanticModel

2.7

2.1

3.9

5019.3

31.7

Off

Off

DataflowGen1

4.8

4.3

5.6

3775.8

13.17

Off

Off

Dataflow Gen2

10.4

9.4

11.9

6402.4

10.27

On

On

DataflowGen2

10.4

9.9

26.4

6899.9

8.54

On

Off

DataflowGen2

20.5

19.9

21.4

8094.2

6.58

Off

On

DataflowGen2

22.9

21.9

23.9

8278.3

6.04

Off

Off


Výsledek

Z výsledků benchmarku vidíme, že Dataflow Gen1 zůstává pro tento druh operací z nějakého důvodu stále králem. Přirozeně přináší vlastní nevýhody, jako například nemožnost přesunu do destinace, ale pokud tě tato omezení netrápí, funguje skvěle. Zcela nepřekvapivě se celá operace čistě uvnitř Semantic Modelu ukazuje jako vůbec nejrychlejší a nejlevnější.


Na straně Dataflow Gen2 se jasně potvrzuje funkčnost Partitioned Compute. Na tom, zda máš zapnutý nebo vypnutý Modern Engine, vlastně nezáleží. Extrémní maximální doba trvání u kombinace se zapnutým Partitioned Compute a vypnutým Modern Engine představuje pouhou anomálii. Běhy kolem 10 minut tvořily jasnou většinu. Jediný výraznější výkyv nastal při spouštění více dataflows naráz. Izolované běhy naopak dosahovaly nesrovnatelně lepších časů.


Kombinace vypnutého Partitioned Compute se zapnutým Modern Engine ukazuje, že modern engine má sice nějaký vliv, ale výsledek zůstává dost tristní. Vypnutí úplně všeho se pak rovná vyložené katastrofě. Omlouvám se za silná slova, ale normální Dataflow Engine očividně potřebuje ještě kus práce, aby podával smysluplné výkony.


Pozitivní zprávou je, že po zveřejnění těchto výsledků na subredditu Microsoft Fabric se mi ozval samotný Microsoft. Celou věc už checkují, takže snad brzy přijde nějaký zlepšovák.



Comments


bottom of page