KomputeLe cadre de calcul GPU à usage général pour les cartes graphiques de Cross Vendor (AMD, Qualcomm, Nvidia & Friends) |
Rejoignez les appels Discord & Community? Documentation Blog Blog ⌨ Exemples?
Vous trouverez ci-dessous un exemple de multiplication GPU à l'aide des interfaces C ++ et Python Kompute.
Vous pouvez rejoindre la discorde pour des questions / discussions, ouvrir un problème GitHub ou lire la documentation.
L'interface C ++ offre un accès de bas niveau aux composants natifs de Kompute, permettant des optimisations avancées ainsi que de l'extension des composants.
void kompute ( const std::string& shader) {
// 1. Create Kompute Manager with default settings (device 0, first queue and no extensions)
kp::Manager mgr;
// 2. Create and initialise Kompute Tensors through manager
// Default tensor constructor simplifies creation of float values
auto tensorInA = mgr. tensor ({ 2 ., 2 ., 2 . });
auto tensorInB = mgr. tensor ({ 1 ., 2 ., 3 . });
// Explicit type constructor supports uint32, int32, double, float and bool
auto tensorOutA = mgr. tensorT < uint32_t >({ 0 , 0 , 0 });
auto tensorOutB = mgr. tensorT < uint32_t >({ 0 , 0 , 0 });
std::vector<std::shared_ptr<kp::Memory>> params = {tensorInA, tensorInB, tensorOutA, tensorOutB};
// 3. Create algorithm based on shader (supports buffers & push/spec constants)
kp::Workgroup workgroup ({ 3 , 1 , 1 });
std::vector< float > specConsts ({ 2 });
std::vector< float > pushConstsA ({ 2.0 });
std::vector< float > pushConstsB ({ 3.0 });
auto algorithm = mgr. algorithm (params,
// See documentation shader section for compileSource
compileSource (shader),
workgroup,
specConsts,
pushConstsA);
// 4. Run operation synchronously using sequence
mgr. sequence ()
-> record <kp::OpSyncDevice>(params)
-> record <kp::OpAlgoDispatch>(algorithm) // Binds default push consts
-> eval () // Evaluates the two recorded operations
-> record <kp::OpAlgoDispatch>(algorithm, pushConstsB) // Overrides push consts
-> eval (); // Evaluates only last recorded operation
// 5. Sync results from the GPU asynchronously
auto sq = mgr. sequence ();
sq-> evalAsync <kp::OpSyncLocal>(params);
// ... Do other work asynchronously whilst GPU finishes
sq-> evalAwait ();
// Prints the first output which is: { 4, 8, 12 }
for ( const float & elem : tensorOutA-> vector ()) std::cout << elem << " " ;
// Prints the second output which is: { 10, 10, 10 }
for ( const float & elem : tensorOutB-> vector ()) std::cout << elem << " " ;
} // Manages / releases all CPU and GPU memory resources
int main () {
// Define a raw string shader (or use the Kompute tools to compile to SPIRV / C++ header
// files). This shader shows some of the main components including constants, buffers, etc
std::string shader = ( R"(
#version 450
layout (local_size_x = 1) in;
// The input tensors bind index is relative to index in parameter passed
layout(set = 0, binding = 0) buffer buf_in_a { float in_a[]; };
layout(set = 0, binding = 1) buffer buf_in_b { float in_b[]; };
layout(set = 0, binding = 2) buffer buf_out_a { uint out_a[]; };
layout(set = 0, binding = 3) buffer buf_out_b { uint out_b[]; };
// Kompute supports push constants updated on dispatch
layout(push_constant) uniform PushConstants {
float val;
} push_const;
// Kompute also supports spec constants on initalization
layout(constant_id = 0) const float const_one = 0;
void main() {
uint index = gl_GlobalInvocationID.x;
out_a[index] += uint( in_a[index] * in_b[index] );
out_b[index] += uint( const_one * push_const.val );
}
)" );
// Run the function declared above with our raw string shader
kompute (shader);
}
Le package Python fournit une interface interactive de haut niveau qui permet l'expérimentation tout en garantissant des flux de travail élevés de performances et de développement rapide.
from . utils import compile_source # using util function from python/test/utils
def kompute ( shader ):
# 1. Create Kompute Manager with default settings (device 0, first queue and no extensions)
mgr = kp . Manager ()
# 2. Create and initialise Kompute Tensors through manager
# Default tensor constructor simplifies creation of float values
tensor_in_a = mgr . tensor ([ 2 , 2 , 2 ])
tensor_in_b = mgr . tensor ([ 1 , 2 , 3 ])
# Explicit type constructor supports uint32, int32, double, float and bool
tensor_out_a = mgr . tensor_t ( np . array ([ 0 , 0 , 0 ], dtype = np . uint32 ))
tensor_out_b = mgr . tensor_t ( np . array ([ 0 , 0 , 0 ], dtype = np . uint32 ))
assert ( t_data . data_type () == kp . DataTypes . uint )
params = [ tensor_in_a , tensor_in_b , tensor_out_a , tensor_out_b ]
# 3. Create algorithm based on shader (supports buffers & push/spec constants)
workgroup = ( 3 , 1 , 1 )
spec_consts = [ 2 ]
push_consts_a = [ 2 ]
push_consts_b = [ 3 ]
# See documentation shader section for compile_source
spirv = compile_source ( shader )
algo = mgr . algorithm ( params , spirv , workgroup , spec_consts , push_consts_a )
# 4. Run operation synchronously using sequence
( mgr . sequence ()
. record ( kp . OpTensorSyncDevice ( params ))
. record ( kp . OpAlgoDispatch ( algo )) # Binds default push consts provided
. eval () # evaluates the two recorded ops
. record ( kp . OpAlgoDispatch ( algo , push_consts_b )) # Overrides push consts
. eval ()) # evaluates only the last recorded op
# 5. Sync results from the GPU asynchronously
sq = mgr . sequence ()
sq . eval_async ( kp . OpTensorSyncLocal ( params ))
# ... Do other work asynchronously whilst GPU finishes
sq . eval_await ()
# Prints the first output which is: { 4, 8, 12 }
print ( tensor_out_a )
# Prints the first output which is: { 10, 10, 10 }
print ( tensor_out_b )
if __name__ == "__main__" :
# Define a raw string shader (or use the Kompute tools to compile to SPIRV / C++ header
# files). This shader shows some of the main components including constants, buffers, etc
shader = """
#version 450
layout (local_size_x = 1) in;
// The input tensors bind index is relative to index in parameter passed
layout(set = 0, binding = 0) buffer buf_in_a { float in_a[]; };
layout(set = 0, binding = 1) buffer buf_in_b { float in_b[]; };
layout(set = 0, binding = 2) buffer buf_out_a { uint out_a[]; };
layout(set = 0, binding = 3) buffer buf_out_b { uint out_b[]; };
// Kompute supports push constants updated on dispatch
layout(push_constant) uniform PushConstants {
float val;
} push_const;
// Kompute also supports spec constants on initalization
layout(constant_id = 0) const float const_one = 0;
void main() {
uint index = gl_GlobalInvocationID.x;
out_a[index] += uint( in_a[index] * in_b[index] );
out_b[index] += uint( const_one * push_const.val );
}
"""
kompute ( shader )Vous pouvez essayer les ordinateurs portables Colab interactifs qui vous permettent d'utiliser un GPU gratuit. Les exemples disponibles sont les exemples Python et C ++ ci-dessous:
Essayez le COLAB C ++ interactif de Blog Blog | Essayez l'interactif Python Colab de l'article de blog |
Vous pouvez également consulter les deux conférences suivantes présentées lors de la conférence Fosdem 2021.
Les deux vidéos ont des horodatages qui vous permettront de passer à la section la plus pertinente pour vous - l'intro et les motivations pour les deux sont presque les mêmes afin que vous puissiez passer au contenu plus spécifique.
Regardez la vidéo pour les amateurs de C ++ | Regardez la vidéo pour les amateurs de Python et d'apprentissage automatique |
L'architecture centrale de Kompute comprend les éléments suivants:
Pour voir une ventilation complète, vous pouvez lire plus loin dans la référence de la classe C ++.
| Architecture complète | Composants de kompute simplifiés |
|---|---|
(Très minuscule, vérifiez le diagramme de référence complet dans les documents pour plus de détails) |
Kompute offre une flexibilité pour exécuter les opérations de manière asynrchone à travers VK :: Fences. En outre, Kompute permet une allocation explicite des files d'attente, qui permettent une exécution parallèle des opérations entre les familles de files d'attente.
L'image ci-dessous fournit une intuition sur la façon dont les séquences de Kompute peuvent être allouées à différentes files d'attente pour activer l'exécution parallèle en fonction du matériel. Vous pouvez voir l'exemple pratique, ainsi que la page de documentation détaillée décrivant comment cela fonctionnerait en utilisant un NVIDIA 1650 comme exemple.
Kompute a été optimisé pour travailler dans des environnements mobiles. Le système de construction permet un chargement dynamique de la bibliothèque partagée Vulkan pour les environnements Android, ainsi qu'un wrapper Android NDK fonctionnel pour les en-têtes CPP.
Pour une plongée profonde, vous pouvez lire le billet de blog "Superchargez vos applications mobiles avec un apprentissage automatique accéléré sur les GPU". Vous pouvez également accéder à l'exemple de code de bout en bout dans le référentiel, qui peut être exécuté à l'aide d'Android Studio. |
Outre le SDK C ++ Core, vous pouvez également utiliser le package Python de Kompute, qui expose la même fonctionnalité de base, et prend en charge l'interopérabilité avec des objets Python comme les listes, les tableaux Numpy, etc.
Les seules dépendances sont Python 3.5+ et CMake 3.4.1+. Vous pouvez installer Kompute à partir du package Python PYPI à l'aide de la commande suivante.
pip install kp
Vous pouvez également installer à partir de Master Branch en utilisant:
pip install git+git://github.com/KomputeProject/kompute.git@master
Pour plus de détails, vous pouvez lire la documentation du package Python ou la documentation de référence de la classe Python.
Le système de construction fourni utilise cmake , qui permet des versions multiplateformes.
Le Makefile de niveau supérieur fournit un ensemble de configurations optimisées pour le développement ainsi que la construction d'image Docker, mais vous pouvez démarrer une version avec la commande suivante:
cmake -Bbuild
Vous pouvez également ajouter Kompute dans votre référentiel avec add_subdirectory - l'exemple Android CMakelists.txt montre comment cela serait fait.
Pour un aperçu plus avancé de la configuration de build, consultez la documentation de plongée profonde du système de construction.
Nous apprécions les PR et les problèmes. Si vous souhaitez contribuer, essayez de vérifier la balise "bon premier problème", mais même l'utilisation de Kompute et des problèmes de rapport est une grande contribution!
Si vous souhaitez exécuter avec des calques de débogage, vous pouvez les ajouter avec le paramètre KOMPUTE_ENV_DEBUG_LAYERS comme:
export KOMPUTE_ENV_DEBUG_LAYERS="VK_LAYER_LUNARG_api_dump"
Pour mettre à jour la documentation, vous devrez:
make push_docs_to_ghpages L'exécution des tests unitaires a été significativement simplifiée pour les contributeurs.
Les tests s'exécutent sur CPU et peuvent être déclenchés à l'aide de l'interface de ligne de commande ACT (https://github.com/nektos/ACT) - Une fois que vous avez installé la ligne de commande (et démarrer le démon docker), vous n'avez qu'à taper:
$ act
[Python Tests/python-tests] Start image=axsauze/kompute-builder:0.2
[C++ Tests/cpp-tests ] Start image=axsauze/kompute-builder:0.2
[C++ Tests/cpp-tests ] ? docker run image=axsauze/kompute-builder:0.2 entrypoint=["/usr/bin/tail" "-f" "/dev/null"] cmd=[]
[Python Tests/python-tests] ? docker run image=axsauze/kompute-builder:0.2 entrypoint=["/usr/bin/tail" "-f" "/dev/null"] cmd=[]
...
Le référentiel contient des tests unitaires pour le code C ++ et Python et peut être trouvé dans le dossier test/ et python/test .
Les tests sont actuellement effectués via le CI à l'aide d'actions GitHub. Il utilise les images trouvées dans docker-builders/ .
Afin de minimiser les exigences matérielles, les tests peuvent fonctionner sans GPU, directement dans le CPU à l'aide de SwiftShader.
Pour plus d'informations sur la configuration de l'IC et des tests, vous pouvez accéder à la section CI, Docker et Tests dans la documentation.
Ce projet a commencé après avoir vu que de nombreux projets ML & DL nouveaux et renommés comme Pytorch, Tensorflow, Alibaba DNN, Tencent NCNN - entre autres - ont soit intégré ou cherchent à intégrer le SDK Vulkan pour ajouter un support de GPU mobile (et transversal).
Le SDK Vulkan propose une excellente interface de bas niveau qui permet des optimisations hautement spécialisées - mais elle a un coût de code hautement verbeux qui nécessite 500-2000 lignes de code pour même commencer à écrire du code d'application. Cela a fait que chacun de ces projets devait mettre en œuvre la même ligne de base pour résumer les fonctionnalités non liées à la compréhension du SDK Vulkan. Cette grande quantité de chaudière non standardisée peut entraîner un transfert de connaissances limité, des chances plus élevées de bogues de mise en œuvre de framework uniques en cours d'introduction, etc.
Nous développons actuellement Kompute pour ne pas masquer l'interface Vulkan SDK (car elle est incroyablement bien conçue) mais pour l'augmenter avec un accent direct sur les capacités informatiques du GPU du SDK Vulkan. Cet article donne un aperçu de haut niveau des motivations de Kompute, ainsi qu'un ensemble d'exemples pratiques qui introduisent à la fois l'informatique GPU ainsi que l'architecture de base de Kompute.