InseeFrLab/utilitR

Ajouter un warning sur la notion de row group size et sur le paramètre min_rows_per_group de write_dataset

Closed this issue · 4 comments

nbc commented

Le problème

arrow::write_dataset ne fixe pas de limite basse au row group size, ce qui peut générer une dégradation très significative des performances en terme de temps de calcul et catastrophique en terme d'utilisation mémoire.

Les résultats d'une requête identique sur fideli_individu21.parquet avec :

  • fideli_insee : d'origine fourni par l'INSEE
  • fideli_groupby : partitionné par dplyr::group_by
  • fideli_partitioning : partitionné avec l'argument partitioning de arrow::write_dataset
  • fideli_100000 : partionné avec dplyr::group_by en fixant l'argument min_rows_per_group de arrow::write_dataset à 100 000
  fname                             duration error  max_mem
  <chr>                                <dbl> <chr>    <dbl>
1 "fideli(fideli_insee)"                2.58 NA      747032
2 "fideli(\"fideli_groupby\")"         26.7  NA    11651424
3 "fideli(\"fideli_partitioning\")"    27.1  NA    11604048
4 "fideli(\"fideli_100000\")"           1.50 NA      557280

(voir le code ci-dessous pour reproduire)

Le code pour refaire le test

Le code pour générer les datasets :

library(arrow)
library(dplyr)

fideli_insee <- "fideli_individu21.parquet"

open_dataset(fideli_insee) |>
  write_dataset("fideli_partitioning", partitioning = c("csdep"))

open_dataset(fideli_insee) |>
  group_by(csdep) |>
  write_dataset("fideli_groupby")

open_dataset(fideli_insee) |>
  group_by(csdep) |>
  write_dataset("fideli_100000", min_rows_per_group = 100000L)

Le script pour refaire les tests (installez https://github.com/nbc/timemoir et redémarrez votre session) :

library(timemoir)
library(arrow)
library(dplyr)

fideli_insee <- "fideli_individu21.parquet"

fideli <- function(file) {
  personnes_mobiles <- open_dataset(file) %>%
    filter(depcom != depcom_n_1 & anais_co < 2004) %>%
    select(poids, anais_co) %>%
    collect()
}

bind_rows(
  timemoir(fideli(fideli_insee)),
  timemoir(fideli("fideli_groupby")),
  timemoir(fideli("fideli_partitioning")),
  timemoir(fideli("fideli_100000"))
)
nbc commented

arrow ne donnant pas (en tout cas je n'ai pas trouvé) de méthode pour accéder au row group size, je colle une fonction utilisant duckdb en dessous et le résultat donne ça :

> parquet_metadata("fideli_individu21.parquet") |> summarize(mean = mean(row_group_num_rows))
# A tibble: 1 × 1
    mean
   <dbl>
1 32754.
> parquet_metadata("fideli_groupby") |> summarize(mean = mean(row_group_num_rows))
# A tibble: 1 × 1
   mean
  <dbl>
1  439.
> parquet_metadata("fideli_partitioning") |> summarize(mean = mean(row_group_num_rows))
# A tibble: 1 × 1
   mean
  <dbl>
1  439.
> parquet_metadata("fideli_100000") |> summarize(mean = mean(row_group_num_rows))
# A tibble: 1 × 1
    mean
   <dbl>
1 64786.
parquet_metadata <- function(data) {
  if (dir.exists(data)) {
    parquet <- file.path(data, '**/*.parquet')
  } else if (file.exists(data)) {
    parquet <- data
  } else {
    stop("parquet doit être un fichier (parquet) ou un répertoire (dataset)")
  }

  tryCatch(
    {
      con <- DBI::dbConnect(duckdb::duckdb())
      DBI::dbGetQuery(con, glue::glue("SELECT * EXCLUDE(key_value_metadata) FROM parquet_metadata('{parquet}')")) |>
        tibble::as_tibble()
    },
    finally = {
      DBI::dbDisconnect(con, shutdown = TRUE)
    }
  )
}
nbc commented

Bon, je soupçonne un pb j'ai fait une issue

nbc commented

J'ai enfin trouvé une solution full Rarrow pour avoir une idée du group group size d'un fichier parquet (en l'occurrence la moyenne) :

> file_path <- "/nfs/partage-r-sas/projet-parquet/fideli/2021/local_for_loop/csdep=75/part-0.parquet"
> a <- ParquetFileReader$create(file_path)
> a$num_rows / a$num_row_groups
[1] 50355.39
> file_path <- "/nfs/partage-r-sas/projet-parquet/fideli/2021/local/csdep=75/part-0.parquet"
> a <- ParquetFileReader$create(file_path)
> a$num_rows / a$num_row_groups
[1] 1150.82

Le premier est bon, le second catastrophique.

introduit par d5f7809