תכנות GPU עם C ++

Gpu Programming With C



במדריך זה נחקור את העוצמה של תכנות GPU עם C ++. מפתחים יכולים לצפות לביצועים מדהימים עם C ++, וגישה לכוח הפנומנלי של ה- GPU עם שפה ברמה נמוכה יכולה להניב כמה מהחישוב המהיר ביותר הקיים כיום.

דרישות

אף על פי שכל מכונה המסוגלת להריץ גרסה מודרנית של לינוקס יכולה לתמוך במהדר C ++, תזדקק ל- GPU מבוסס NVIDIA כדי לבצע את התרגיל הזה. אם אין לך GPU, תוכל לסובב מופע המופעל באמצעות GPU בשירותי האינטרנט של אמזון או ספק ענן אחר לבחירתך.







אם תבחר מכונה פיזית, ודא שהתקנת את מנהלי ההתקן הקנייניים של NVIDIA. תוכל למצוא הנחיות לכך כאן: https://linuxhint.com/install-nvidia-drivers-linux/



בנוסף לנהג, תזדקק לערכת הכלים של CUDA. בדוגמה זו נשתמש באובונטו 16.04 LTS, אך קיימות הורדות זמינות לרוב ההפצות הגדולות בכתובת האתר הבאה: https://developer.nvidia.com/cuda-downloads



עבור אובונטו, היית בוחר בהורדה מבוססת .deb. לקובץ שהורדת לא תהיה סיומת .deb כברירת מחדל, לכן אני ממליץ לשנות את שמו לקובץ .deb בסוף. לאחר מכן, תוכל להתקין באמצעות:





סודו dpkg -אניpackage-name.deb

סביר להניח שתתבקש להתקין מפתח GPG, ואם כן, פעל על פי ההנחיות שסופקו לשם כך.

לאחר שתעשה זאת, עדכן את המאגרים שלך:



סודו עדכון apt-get
סודו apt-get להתקיןניסים

לאחר סיום, אני ממליץ לאתחל מחדש כדי לוודא שהכל נטען כראוי.

היתרונות של פיתוח GPU

מעבדים מטפלים בכניסות ויציאות רבות ומכילות מבחר גדול של פונקציות לא רק להתמודד עם מגוון רחב של צרכי התוכנית אלא גם לניהול תצורות חומרה שונות. הם גם מטפלים בזיכרון, במטמון, באוטובוס המערכת, בפילוח ובפונקציונליות של IO, מה שהופך אותם לשקע של כל העסקאות.

מעבד GPU הפוך - הם מכילים מעבדים בודדים רבים המתמקדים בפונקציות מתמטיות פשוטות מאוד. בגלל זה, הם מעבדים משימות מהר הרבה יותר מאשר מעבדים. על ידי התמחות בפונקציות סקלריות (פונקציה שלוקחת כניסה אחת או יותר אך מחזירה רק פלט יחיד), הם משיגים ביצועים קיצוניים במחיר של התמחות קיצונית.

קוד לדוגמא

בקוד הדוגמה, אנו מוסיפים וקטורים יחד. הוספתי גרסת מעבד ו- GPU של הקוד להשוואת מהירות.
gpu-example.cpp התוכן למטה:

#כלול 'cuda_runtime.h'
#לִכלוֹל
#לִכלוֹל
#לִכלוֹל
#לִכלוֹל
#לִכלוֹל

typedefשעה (ות::כרונו::שעון ברזולוציה גבוההשָׁעוֹן;

#define ITER 65535

// גרסת מעבד של פונקציית הוספת וקטור
בָּטֵל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,
// כך cudaMallocManaged מספק את אלה
cudaMallocManaged(&gpu_a, ITER* מידה של(int));
cudaMallocManaged(&gpu_b, ITER* מידה של(int));
cudaMallocManaged(&gpu_c, ITER* מידה של(int));

ל (intאני= 0;אני<ITER; ++אני) {
ל[אני] =אני;
ב[אני] =אני;
ג[אני] =אני;
}

// התקשרו לפונקציית המעבד והזמינו אותה
אוטומטי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
cudaFree(ל);
cudaFree(ב);
cudaFree(ג);

// שחרר את הקצאות הזיכרון המבוססות על פונקציות המעבד
חינם(ל);
חינם(ב);
חינם(ג);

לַחֲזוֹר 0;
}

קובץ Makefile התוכן למטה:

INC= -אני/usr/מְקוֹמִי/ניסים/לִכלוֹל
NVCC=/usr/מְקוֹמִי/ניסים/אני/nvcc
NVCC_OPT= -std = c ++אחת עשרה

את כל:
$(NVCC)$(NVCC_OPT)gpu-example.cpp-אוֹgpu- דוגמה

לְנַקוֹת:
-רם -fgpu- דוגמה

כדי להריץ את הדוגמה, ריכז אותה:

עשה

לאחר מכן הפעל את התוכנית:

./gpu- דוגמה

כפי שאתה יכול לראות, גרסת המעבד (vector_add_cpu) פועלת לאט בהרבה מגירסת ה- GPU (vector_add_gpu).

אם לא, ייתכן שיהיה עליך להתאים את הגדרת ITER ב- gpu-example.cu למספר גבוה יותר. הסיבה לכך היא שזמן ההתקנה של ה- GPU ארוך יותר מכמה לולאות קטנות יותר העומדות על מעבד. מצאתי 65535 שעובד היטב במכונה שלי, אך הקילומטראז 'שלך עשוי להשתנות. עם זאת, ברגע שאתה מנקה את הסף הזה, ה- GPU מהיר יותר באופן דרמטי מהמעבד.

סיכום

אני מקווה שלמדת הרבה מההקדמה שלנו לתכנות GPU עם C ++. הדוגמה שלמעלה לא משיגה הרבה מאוד, אבל המושגים שהוכחו מספקים מסגרת שבה תוכל להשתמש כדי לשלב את הרעיונות שלך כדי לשחרר את העוצמה של ה- GPU שלך.