Sommaire
Demat'i-nal,
La mouture 0.8.7, tendrement nommée skol-loarn de Pythran, est de sortie. Rappelons que Pythran est un compilateur dédié au calcul scientifique pour Python. Il s'installe avec pip
ou conda
et nécessite juste un compilateur C++ qui parle le dialecte c++11
sur l'hôte. Car oui, Pythran fait partie de cette ignoble lignée des transpileurs…
Quelques liens utiles :
- Page GitHub du projet : https://github.com/serge-sans-paille/pythran
- La doc officielle : http://pythran.readthedocs.io/
- Pythran sur PyPI : https://pypi.org/project/pythran/
- Pythran sur Conda-forge : https://anaconda.org/conda-forge/pythran
- Des histoires pythranesques : http://serge-sans-paille.github.io/pythran-stories/
- Le changlog : https://pythran.readthedocs.io/en/latest/Changelog.html
Le reste de ma prose va détailler deux nouveautés qui viennent étendre le langage (strictement inclus dans Python) que Pythran supporte.
static if
Il est assez courant d'utiliser des constructions du type if a is None: do_stuff()
en Python. C'est une façon (que je trouve) élégante d'implémenter un option type. Mais comment traduire ça de manière fortement typée pour des cas comme celui-là :
def foo(x):
if x is None or x < 0:
y = 1
else:
y = x + 3
return y ** 2
La stratégie habituellement utilisée par Pythran est de générer un code ressemblant à ça :
template<class T>
auto foo(T x) -> decltype((is_(x, None) || x < 0)?(1**2):((x+3)**2))
{
decltype((is_(x, None) || x < 0)?(1):((x+3))) y;
if (is_(x, None) || x < 0)
y = 1;
else
y = x + 3;
return y ** 2;
}
Ou presque (en vrai, le calcul de type est plus complexe, l'opérateur or
est plus complexe). Mais quel type donné à l'expression (is_(x, None) || x < 0)?(1):((x+3))
si foo
est appelé avec None
en paramètre ? Dans ce cas x+3
n'a tout simplement pas de sens…
C++17 fournit une solution au problème à travers le static if
, qui donnerait un truc du genre :
template<class T>
auto foo(T x)
{
static if (is_(x, None)) {
if(x < 0) {
auto y = 1;
return y ** 2;
}
else {
auto y = x + 3;
return y ** 2;
}
}
else {
auto y = x + 3;
return y ** 2;
}
}
en supposant que is_
est une fonction constexpr
. On remarquera que le flot de contrôle a un peu changé, que cette transformation est difficile à généraliser si il y a des boucles, et qu'elle repose également sur l'inférence de type de retour de fonction de C++14. Pythran émule donc ce comportement en C++11, la mécanique pour y arriver est bien plus complexe que celle proposée dans cet article de blog qui ne gère pas les return
dans le bloc gardé, mais qui s'en inspire. Un gros merci à Yann Diorcet pour avoir motiver ces devs.
tableau à dimensions partiellements fixées
Pour traiter, par exemple, une image en RGB, Pythran a toujours été un peu à la traine par rapport à cython. En effet, si on veut faire la moyenne de deux images en excluant les bords, on peut utiliser :
#pythran export average(uint8[:,:,:], uint8[:,:,:])
def average(x, y):
return x[1:-1,1:-1] / 2 + y[1:-1,1:-1] / 2 # avoid saturation at the expense of a small difference
Mais en l'absence de plus d'information, impossible pour Pythran de savoir qu'il n'y aura que trois pixels dans la dernière dimension, et qu'on peut (p.e.) dérouler le parcours de cette dernière. Alors qu'en rendant les boucles explicites, c'est facile:
#pythran export average(uint8[:,:,:], uint8[:,:,:])
def average(x, y):
m, n, _ = x.shape
out = np.empty((m,n,3))
for i in range(1, m-1):
for j in range(1, n-1):
for k in range(3):
out[i - 1, j - 1, k] = x[i,j,k] / 2 + y[i,j,k] / 2
return out
Les deux codes sont valides en Pythran mais le deuxième sent un peu sous les aisselles. Grosse source de frustration pour les habitués de la programmation de haut niveau que de devoir expliciter ces boucles, ces indices. On se sent un peu sale.
Et bien en utilisant la technique présentée dans un journal précédent, Pythran résout élégamment ce problème et il est maintenant possible d'écrire
#pythran export average(uint8[:,:,3], uint8[:,:,3])
def average(x, y):
return x[1:-1,1:-1] / 2 + y[1:-1,1:-1] / 2
On n'arrive malheureusement pas encore au niveau des performances d'un code avec boucles explicites, mais la mécanique est là, et l'envie aussi :-)
Pas futurs
Les deux points évoqués dans ce journal ouvrent des perspectives intéressantes :
- support de
isinstance
qui pourra utiliser la même mécanique que pouris None
; - améliorer le code généré et le runtime utilisé par Pythran pour que le code sans boucle soit aussi performant que le code avec.
Et aussi (et surtout) l'abandon de boost.simd
qui est bien moribond au profit de xsimd.
# Edit
Posté par serge_sans_paille (site web personnel) . Évalué à 1.
Mouarf, je me suis vautré sur l'indentation des listes à puces, si un modo peut modifier ça, c'est cool !
[^] # Re: Edit
Posté par claudex . Évalué à 2.
C'est corrigé.
« Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche
# static if -> if constexpr
Posté par rewind (Mastodon) . Évalué à 7.
On ne dit plus
static if
, on ditif constexpr
. Le vocabulaire a évolué pendant la normalisation. Et commeconstexpr
concerne tout ce qui est calculé à la compilation, le comité a trouvé plus logique de direif constexpr
(if
calculé à la compilation).[^] # Re: static if -> if constexpr
Posté par serge_sans_paille (site web personnel) . Évalué à 3.
Tu as… carrément raison :-) Merci pour la piqure de rappel, les exemples ont été écrits de tête, je serais surpris que ce soit la seule erreur…
# simd explicite ?
Posté par Nicolas Boulay (site web personnel) . Évalué à 4.
Pourquoi utiliser du SIMD explicite ? Les intrasec et vectorisation automatique de GCC ne suffisent pas ?
"La première sécurité est la liberté"
[^] # Re: simd explicite ?
Posté par serge_sans_paille (site web personnel) . Évalué à 3.
Malheureusement non. Petit exemple qui illustre mes propos : https://godbolt.org/z/pNNPhx
[^] # Re: simd explicite ?
Posté par Nicolas Boulay (site web personnel) . Évalué à 3. Dernière modification le 20 septembre 2018 à 12:32.
Je ne comprends pas trop l"exemple. Il n'existe pas d'instructions SIMD pour cos(). Sinon, Le code de l'addition est correctement vectorisé, non ?
"La première sécurité est la liberté"
[^] # Re: simd explicite ?
Posté par serge_sans_paille (site web personnel) . Évalué à 3.
Ce qui répond un peu à la question :-) Il y a plusieurs libs qui fournissent une version vectorisée de la makorité des fonctions de la lib math, p.e. https://software.intel.com/en-us/node/524352, http://sleef.org/, ou https://github.com/QuantStack/xsimd. Pythran utilise ces libs car le compilo ne le fait pas (mais ça bouge, et llvm sait représenter ça au niveau IR, cf. https://reviews.llvm.org/D24951)
[^] # Re: simd explicite ?
Posté par Nicolas Boulay (site web personnel) . Évalué à 3.
Mais appeler ses libs ne demandent pas d'utiliser des intrasecs SIMD, si ?
C'est vrai aussi que les compilo ont rarement remplacé des bouts de code, par des fonctions optimisé, en dehors de memcpy().
"La première sécurité est la liberté"
[^] # Re: simd explicite ?
Posté par serge_sans_paille (site web personnel) . Évalué à 3.
Pour xsimd, non (la lib fournit une abstraction des registres vectoriels).
Pour sleef, elle fournit de nouvelles fonctions compatibles avec l'usage d'intrinsèques, comme le montre cet exemple tiré de leur doc
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.