Προγραμματισμός GPU με C ++

Gpu Programming With C



Σε αυτόν τον οδηγό, θα διερευνήσουμε τη δύναμη του προγραμματισμού GPU με C ++. Οι προγραμματιστές μπορούν να αναμένουν απίστευτη απόδοση με C ++ και η πρόσβαση στην εκπληκτική ισχύ της GPU με γλώσσα χαμηλού επιπέδου μπορεί να αποφέρει μερικούς από τους ταχύτερους υπολογισμούς που διατίθενται αυτήν τη στιγμή.

Απαιτήσεις

Ενώ κάθε μηχάνημα ικανό να τρέξει μια σύγχρονη έκδοση Linux μπορεί να υποστηρίξει έναν μεταγλωττιστή C ++, θα χρειαστείτε μια GPU που βασίζεται σε NVIDIA για να ακολουθήσετε αυτήν την άσκηση. Εάν δεν έχετε GPU, μπορείτε να περιστρέψετε μια παρουσία που υποστηρίζεται από GPU στις Υπηρεσίες Ιστού Amazon ή σε άλλο πάροχο cloud της επιλογής σας.







Εάν επιλέξετε ένα φυσικό μηχάνημα, βεβαιωθείτε ότι έχετε εγκαταστήσει τα αποκλειστικά προγράμματα οδήγησης NVIDIA. Μπορείτε να βρείτε οδηγίες για αυτό εδώ: https://linuxhint.com/install-nvidia-drivers-linux/



Εκτός από το πρόγραμμα οδήγησης, θα χρειαστείτε την εργαλειοθήκη CUDA. Σε αυτό το παράδειγμα, θα χρησιμοποιήσουμε το Ubuntu 16.04 LTS, αλλά υπάρχουν διαθέσιμες λήψεις για τις περισσότερες μεγάλες διανομές στην ακόλουθη διεύθυνση URL: https://developer.nvidia.com/cuda-downloads



Για το Ubuntu, θα επιλέγατε τη λήψη με βάση .deb. Το ληφθέν αρχείο δεν θα έχει επέκταση .deb από προεπιλογή, επομένως προτείνω να μετονομάσετε σε .deb στο τέλος. Στη συνέχεια, μπορείτε να εγκαταστήσετε με:





sudo dpkg -Εγώpackage-name.deb

Πιθανότατα θα σας ζητηθεί να εγκαταστήσετε ένα κλειδί GPG και αν ναι, ακολουθήστε τις οδηγίες που παρέχονται για να το κάνετε.

Μόλις το κάνετε αυτό, ενημερώστε τα αποθετήρια σας:



sudo apt-get ενημέρωση
sudo apt-get installθαύματα-και

Μόλις τελειώσω, προτείνω επανεκκίνηση για να διασφαλίσετε ότι όλα είναι σωστά φορτωμένα.

Τα οφέλη της ανάπτυξης GPU

Οι επεξεργαστές χειρίζονται πολλές διαφορετικές εισόδους και εξόδους και περιέχουν μια μεγάλη ποικιλία λειτουργιών όχι μόνο για την αντιμετώπιση μιας μεγάλης ποικιλίας αναγκών του προγράμματος, αλλά και για τη διαχείριση διαφόρων διαμορφώσεων υλικού. Χειρίζονται επίσης τη μνήμη, την προσωρινή αποθήκευση, το δίαυλο συστήματος, την τμηματοποίηση και τη λειτουργικότητα IO, καθιστώντας τους ένα βύσμα όλων των συναλλαγών.

Οι GPU είναι το αντίθετο - περιέχουν πολλούς μεμονωμένους επεξεργαστές που επικεντρώνονται σε πολύ απλές μαθηματικές λειτουργίες. Εξαιτίας αυτού, επεξεργάζονται εργασίες πολλές φορές γρηγορότερα από τους επεξεργαστές. Με εξειδίκευση στις κλιμακωτές συναρτήσεις (μια λειτουργία που λαμβάνει μία ή περισσότερες εισόδους αλλά επιστρέφει μόνο μία έξοδο), επιτυγχάνουν ακραίες επιδόσεις με κόστος ακραίας εξειδίκευσης.

Παράδειγμα κώδικα

Στο παράδειγμα κώδικα, προσθέτουμε διανύσματα μαζί. Έχω προσθέσει μια έκδοση CPU και GPU του κώδικα για σύγκριση ταχύτητας.
gpu-example.cpp περιεχόμενο παρακάτω:

#include 'cuda_runtime.h'
#περιλαμβάνω
#περιλαμβάνω
#περιλαμβάνω
#περιλαμβάνω
#περιλαμβάνω

typedefώρες::χρονο::high_resolution_clockΡολόι?

#define ITER 65535

// Έκδοση CPU της συνάρτησης προσθήκης διανύσματος
κενόςvector_add_cpu(int *προς το,int *σι,int *ντο,intν) {
intΕγώ?

// Προσθέστε τα διανυσματικά στοιχεία a και b στο διάνυσμα c
Για (Εγώ= 0?Εγώ<ν? ++Εγώ) {
ντο[Εγώ] =προς το[Εγώ] +σι[Εγώ]?
}
}

// Έκδοση GPU της συνάρτησης προσθήκης διανύσματος
__παγκόσμια__κενόςvector_add_gpu(int *gpu_a,int *gpu_b,int *gpu_c,intν) {
intΕγώ=threadIdx.Χ?
// Δεν απαιτείται βρόχος επειδή ο χρόνος εκτέλεσης CUDA
// θα περάσει αυτό το ITER φορές
gpu_c[Εγώ] =gpu_a[Εγώ] +gpu_b[Εγώ]?
}

intκύριος() {

int *προς το,*σι,*ντο?
int *gpu_a,*gpu_b,*gpu_c?

προς το= (int *)malloc(ITER* μέγεθος του(int))?
σι= (int *)malloc(ITER* μέγεθος του(int))?
ντο= (int *)malloc(ITER* μέγεθος του(int))?

// Χρειαζόμαστε μεταβλητές προσβάσιμες στη GPU,
// έτσι διαφορετικάMallocManaged παρέχει αυτά
διαφορετικόMallocManaged(&gpu_a, ITER* μέγεθος του(int))?
διαφορετικόMallocManaged(&gpu_b, ITER* μέγεθος του(int))?
διαφορετικόMallocManaged(&gpu_c, ITER* μέγεθος του(int))?

Για (intΕγώ= 0?Εγώ<ITER? ++Εγώ) {
προς το[Εγώ] =Εγώ?
σι[Εγώ] =Εγώ?
ντο[Εγώ] =Εγώ?
}

// Καλέστε τη λειτουργία της CPU και χρονομετρήστε την
αυτοcpu_start=Ρολόι::τώρα()?
vector_add_cpu(a, b, c, ITER)?
αυτοcpu_end=Ρολόι::τώρα()?
ώρες::κόστος << 'vector_add_cpu:'
<<ώρες::χρονο::διάρκεια_μετάδοσης<ώρες::χρονο::νανοδευτερόλεπτα>>(cpu_end-cpu_start)Τομετρώ()
<< «νανοδευτερόλεπτα. n'?

// Καλέστε τη λειτουργία GPU και χρονομετρήστε την
// Οι βραχίονες τριπλής γωνίας είναι μια επέκταση χρόνου εκτέλεσης CUDA που επιτρέπει
// παράμετροι μιας κλήσης πυρήνα CUDA που πρόκειται να περάσει.
// Σε αυτό το παράδειγμα, περνάμε ένα μπλοκ νήματος με νήματα ITER.
αυτοgpu_start=Ρολόι::τώρα()?
vector_add_gpu<<<1, ITER>>> (gpu_a, gpu_b, gpu_c, ITER)?
cudaDeviceSynchronize()?
αυτοgpu_end=Ρολόι::τώρα()?
ώρες::κόστος << 'vector_add_gpu:'
<<ώρες::χρονο::διάρκεια_μετάδοσης<ώρες::χρονο::νανοδευτερόλεπτα>>(gpu_end-gpu_start)Τομετρώ()
<< «νανοδευτερόλεπτα. n'?

// Δωρεάν τις εκχωρήσεις μνήμης που βασίζονται στη λειτουργία GPU
διαφορετικό Δωρεάν(προς το)?
διαφορετικό Δωρεάν(σι)?
διαφορετικό Δωρεάν(ντο)?

// Ελευθερώστε τις κατανομές μνήμης που βασίζονται στη λειτουργία CPU
Ελεύθερος(προς το)?
Ελεύθερος(σι)?
Ελεύθερος(ντο)?

ΕΠΙΣΤΡΟΦΗ 0?
}

Makefile περιεχόμενο παρακάτω:

INC= -Ι/usr/τοπικός/θαύματα/περιλαμβάνω
NVCC=/usr/τοπικός/θαύματα/είμαι/nvcc
NVCC_OPT= -std = c ++έντεκα

όλα:
$(NVCC)$(NVCC_OPT)gpu-example.cppgpu-παράδειγμα

ΚΑΘΑΡΗ:
-ρμ -φάgpu-παράδειγμα

Για να εκτελέσετε το παράδειγμα, μεταγλωττίστε το:

φτιαχνω, κανω

Στη συνέχεια, εκτελέστε το πρόγραμμα:

Το/gpu-παράδειγμα

Όπως μπορείτε να δείτε, η έκδοση της CPU (vector_add_cpu) εκτελείται πολύ πιο αργά από την έκδοση GPU (vector_add_gpu).

Εάν όχι, ίσως χρειαστεί να προσαρμόσετε τον ορισμό ITER στο gpu-example.cu σε μεγαλύτερο αριθμό. Αυτό οφείλεται στο γεγονός ότι ο χρόνος εγκατάστασης της GPU είναι μεγαλύτερος από ορισμένους μικρότερους βρόχους έντασης CPU. Βρήκα το 65535 να λειτουργεί καλά στο μηχάνημά μου, αλλά τα χιλιόμετρα μπορεί να διαφέρουν. Ωστόσο, μόλις καθαρίσετε αυτό το όριο, η GPU είναι δραματικά ταχύτερη από την CPU.

συμπέρασμα

Ελπίζω να μάθατε πολλά από την εισαγωγή μας στον προγραμματισμό GPU με C ++. Το παραπάνω παράδειγμα δεν επιτυγχάνει πολλά, αλλά οι έννοιες που παρουσιάζονται παρέχουν ένα πλαίσιο που μπορείτε να χρησιμοποιήσετε για να ενσωματώσετε τις ιδέες σας για να απελευθερώσετε τη δύναμη της GPU σας.