Сборка CUDA из нескольких объектных файлов

Достаточно много крови выпила эта сборка ;)!
Задача:
- обычно примеры CUDA показывают в одном небольшом файле...
- а вот когда пример разростается, становится более реальным приложением, его хорошо бы собирать из нескольких исходных файлов кода, которые компилируются отдельно в объектную форму, а потом линкуются вместе в общее приложение...

Я попытаюсь приложить файл проекта, чтобы не объяснять всё-всё...

AttachmentSize
Plain text icon m.txt9.76 KB

Forums: 

Выше файл m.txt - это,

Выше файл m.txt - это, конечно, никакой не .txt - это m.tgz, переименуйте его и разархивируйте... или переименуйте в m.zip и он так же замечательно разархивируется WinZip.

Так вот о чём речь ... сразу показываю Makefile:

  1. ALL      = solid tstcc multiple
  2.  
  3. all:    $(ALL)
  4.  
  5. solid:  solid.cu clock.h clock.c
  6.         nvcc solid.cu -o solid
  7.  
  8. multiple: multiple.cu clock.o
  9.         nvcc --compile multiple.cu -o multiple.o
  10.         g++ multiple.o clock.o -L/usr/local/cuda/lib/ -lcudart -o multiple
  11.  
  12. tstcc:  tstcc.cc clock.o
  13.         g++ -c tstcc.cc -o tstcc.o
  14.         g++ tstcc.o clock.o -o tstcc
  15.  
  16. clock.o: clock.h clock.c
  17.         g++ -c clock.c -o clock.o
  18.  
  19. clean:
  20.         rm -rf $(ALL) *.o

Нас там касаются 2 цели: solid, когда CUDA приложение собирается из одного файла, и multiple, когда абсолютно то же CUDA приложение собирается из 2-х объектных файлов.

- файл solid.cu : #include

- файл solid.cu :

  1. #include <iostream>
  2. using namespace std;
  3. #include "clock.h"
  4.  
  5. #include "clock.c"  // реализация clock_cycles()
  6.  
  7. // Kernel definition
  8. __global__ void VecAdd( void ) {
  9.     return;
  10. }
  11.  
  12. int main() {
  13.     int N = 100;
  14.     uint64_t t1, t2;
  15.     t1 = clock_cycles();
  16.     // Kernel invocation with N threads
  17.     VecAdd<<<1, N>>>();
  18.     t2 = clock_cycles();
  19.     cout << "execution time was " << ( t2 - t1 ) << " cycles" << endl;
  20. }

Он текстуально (по #include) включает файл C-кода (который должен компилироваться как C++ !) clock.c :

  1. #include "clock.h"
  2.  
  3. uint64_t clock_cycles( void ) {
  4.     uint64_t x;
  5.     asm volatile ( "rdtsc" : "=A" (x) );
  6.     return x;
  7. }

А теперь последняя (надеюсь)

А теперь последняя (надеюсь) вставка - полная альтернатива предыдущему, но тот же clock.c компилируется отдельно, а multiple.cu отдельно, а объектные модули собираются в единое CUDA приложение (см. Makefile):

  1. #include <iostream>
  2. using namespace std;
  3. #include "clock.h"
  4.  
  5. // Kernel definition
  6. __global__ void VecAdd( void ) {
  7.     return;
  8. }
  9.  
  10. int main() {
  11.     int N = 100;
  12.     uint64_t t1, t2;
  13.     t1 = clock_cycles();
  14.     // Kernel invocation with N threads
  15.     VecAdd<<<1, N>>>();
  16.     t2 = clock_cycles();
  17.     cout << "execution time was " << ( t2 - t1 ) << " cycles" << endl;
  18. }

Вы мне скажете: "а чем оно отличается"?
А тем и отличается, 1-й строчкой #include, что код clock.c сюда не включается.

Но вопросов этот простенький

Но вопросов этот простенький пример порождает много:

- компиляция C-кода делается g++, хотя бы потому, что C & C++ образуют по-разному внешние имена для линковки:

  1. [<a href="mailto:olej@nvidia" rel="nofollow">olej@nvidia</a> mobj]$ nm clock.o
  2. 00000000 T _Z12clock_cyclesv

вот то _Z12... и есть типизация параметров и возврата C++.

- стоит поменять g++ на gcc на линковке и всё рассыпается ... из-за дефаултных подключаемых библиотек (а как их дефаулт - посмотреть?):

  1. [<a href="mailto:olej@nvidia" rel="nofollow">olej@nvidia</a> mobj]$ ldd multiple
  2.     linux-gate.so.1 =>  (0x0076b000)
  3.     libcudart.so.4 => /usr/local/cuda/lib/libcudart.so.4 (0x00ddc000)
  4.     libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x075d5000)
  5.     libm.so.6 => /lib/libm.so.6 (0x00b51000)
  6.     libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00b88000)
  7.     libc.so.6 => /lib/libc.so.6 (0x009a1000)
  8.     libdl.so.2 => /lib/libdl.so.2 (0x00b4a000)
  9.     libpthread.so.0 => /lib/libpthread.so.0 (0x00b2d000)
  10.     librt.so.1 => /lib/librt.so.1 (0x00b7d000)
  11.     /lib/ld-linux.so.2 (0x0097c000)

- ну и в подтверждение того, что это одно и то же (почти) приложение:

  1.  
  2. [<a href="mailto:olej@nvidia" rel="nofollow">olej@nvidia</a> mobj]$ ls -l *
  3. ...
  4. -rwxrwxr-x. 1 olej olej 11550 Ноя  5 21:09 multiple
  5. ...
  6. -rwxrwxr-x. 1 olej olej 11525 Ноя  5 21:09 solid
  7. ...
  8. [<a href="mailto:olej@nvidia" rel="nofollow">olej@nvidia</a> mobj]$ ./multiple
  9. execution time was 184467452 cycles
  10. [<a href="mailto:olej@nvidia" rel="nofollow">olej@nvidia</a> mobj]$ ./solid
  11. execution time was 132999604 cycles
  12. [<a href="mailto:olej@nvidia" rel="nofollow">olej@nvidia</a> mobj]$ ./solid
  13. execution time was 160662372 cycles
  14. [<a href="mailto:olej@nvidia" rel="nofollow">olej@nvidia</a> mobj]$ ./multiple
  15. execution time was 117963975 cycles