User Tools

Site Tools


fr:prog:py:perf:switch

Switch en Python

Introduction

Il existe dans beaucoup de langages (C/C++, JS…) une structure switch (ou match en Rust). Mais pas en Python. Trouvons le moyen le plus optimisé de la remplacer.

Ici, nous allons traduire ce code en Python:

  for(int i=0; i<n; i++) {
      switch(i%4) {
          case 0: v = random()+1; break;
          case 1: v = random()+2; break;
          case 2: v = random()+3; break;
          default: v = random()+4;
      }
  }

Solutions

Un tableau de comparaison des performances est disponible à la fin.

1: if / elif... / else

  for i in range(n):
      v = 0
      if i%4 == 0:
          v = random()+1
      elif i%4 == 1:
          v = random()+2
      elif i%4 == 2:
          v = random()+3
      else:
          v = random()+4

Voilà l'approche naïve et redondante.

2: dict.get()

  for i in range(n):
      v = {
          0: random()+1,
          1: random()+2,
          2: random()+3
      }.get(i, random()+4)

On utilise un dict et sa méthode get avec une valeur par défaut.

3: try dict[] except

  for i in range(n):
      try:
          v = {
              0: random()+1,
              1: random()+2,
              2: random()+3
          }[i]
      except KeyError:
          v = random()+4

Ce que beaucoup de gens disent sur les bonnes manières de programmer en Python, c'est qu'il est bon d'utiliser des try sans perdre le temps de vérifier les données. Or, cet exemple montre que ce n'est pas toujours vrai.

4: if in dict / else

  t = [0,1,2]
  for i in range(n):
      if i in t:
          v = {
              0: random()+1,
              1: random()+2,
              2: random()+3
          }[i]
      else:
          v = random()+4

Alors on vérifie les données avant et on laisse tomber le try. Au cas où le in serait plus rapide avec une list qu'avec un dict, on essaie avec une list.

5: dict + lambda

  t = [0,1,2]
  f = {
      0: lambda: random()+1,
      1: lambda: random()+2,
      2: lambda: random()+3
  }
  for i in range(n):
      if i in t:
          v = f[i]()
      else:
          v = random()+4

Ne redéclarons pas notre dict à chaque itération grâce aux lambda.

6: dict + lambda #2

  f = {
      0: lambda: random()+1,
      1: lambda: random()+2,
      2: lambda: random()+3
  }
  for i in range(n):
      if i in f:
          v = f[i]()
      else:
          v = random()+4

Et finalement on peut abandonner la list.

Comparatif

Les durées sont donnés en secondes pour n = 50,000,000 (Linux 4.19, x86_64, Intel Core i5 g7, GCC 8.2.1).

Solution Python2.7.16 Python3.7.3
1 8.442 7.273
2 20.957 19.701
3 38.195 27.798
4 5.760 5.505
5 5.793 5.495
6 5.090 4.620

La solution la plus rapide est donc un dict avec des lambda, dans un if. Le try/except est de loin le pire, et la série de if ne donne pas de très bons résultats. Au passage, on peut constater de meilleures performances avec Python3 par rapport à Python2.

fr/prog/py/perf/switch.txt · Last modified: 2019-04-09 19:43 by tuxmain