CUDA রানটাইম এপিআই ব্যবহার করে ত্রুটিগুলি যাচাইয়ের সাধারণ উপায় কী?


258

CUDA প্রশ্নগুলির উত্তর এবং মন্তব্যগুলি এবং CUDA ট্যাগ উইকিতে আমি দেখতে পাই এটি প্রায়শই পরামর্শ দেওয়া হয় যে প্রতিটি এপিআই কলের রিটার্নের স্থিতি ত্রুটিগুলির জন্য পরীক্ষা করা উচিত। এপিআই ডকুমেন্টেশন মতো কাজগুলির রয়েছে cudaGetLastError, cudaPeekAtLastErrorএবং cudaGetErrorString, কিন্তু সবচেয়ে ভালো উপায় অতিরিক্ত কোড প্রচুর প্রয়োজন ছাড়া নির্ভরযোগ্যভাবে ধরা ও প্রতিবেদন ত্রুটি এইসব একত্র করা কি?


13
এনভিডিয়া এর CUDA নমুনার একটি শিরোলেখ, helper_cuda.h, ম্যাক্রো বলা আছে যে রয়েছে getLastCudaErrorএবং checkCudaErrorsযা প্রায় কাছাকাছি কি বর্ণনা করা হয় না গৃহীত উত্তর । বিক্ষোভের জন্য নমুনাগুলি দেখুন। কেবলমাত্র সরঞ্জামকিট সহ নমুনাগুলি ইনস্টল করতে চয়ন করুন এবং আপনার এটি থাকবে।
chappjc

@chappjc আমি এই প্রশ্নটি এবং উত্তরটি মূল বলে ভেবে দেখছি না, যদি এটি আপনি যা বোঝাতে চান তবে এটির শিক্ষাগত লোকেরা সিউডিএর ত্রুটি পরীক্ষা করে ব্যবহার করার যোগ্যতা রাখে।
জ্যাকলান্টারন

পছন্দ করুন এই প্রশ্নোত্তরটি আমার পক্ষে খুব সহায়ক ছিল এবং এসডিকে কিছু শিরোনামের চেয়ে এটি খুঁজে পাওয়া অবশ্যই সহজ। আমি এনভিআইডিএ এটি কীভাবে পরিচালনা করে এবং আরও কীভাবে সন্ধান করা যায় তাও এটি উল্লেখ করা মূল্যবান বলে আমি মনে করি। আমি যদি পারতাম তবে আমার মন্তব্যের সুরটি নরম করতাম। :)
chappjc

ডিবাগিং সরঞ্জামগুলি আপনাকে "অ্যাপ্রোচ" করতে দেয় যেখানে ত্রুটিগুলি শুরু হয় CUDA তে ২০১২ সাল থেকে দুর্দান্ত উন্নতি হয়েছে। আমি জিইউআই ভিত্তিক ডিবাগারগুলির সাথে কাজ করি নি তবে সিইউডিএ ট্যাগ উইকি কমান্ড লাইন চুদা-জিডিবি উল্লেখ করেছে। এটি একটি খুব শক্তিশালী সরঞ্জাম কারণ এটি আপনাকে জিপিইউতে প্রকৃত ওয়ার্পস এবং থ্রেডগুলির মধ্য দিয়ে যাওয়ার অনুমতি দেয় (যদিও বেশিরভাগ সময় 2.0+ আর্কিটেকচারের প্রয়োজন হয়)
opetrenko

@ ব্লুয়েফট: আপনি যে সম্পাদনাটি ফিরে এসেছিলেন তা নিয়ে কী ছিল? দেখে মনে হচ্ছে আসলে মার্কডাউনে কিছুই পরিবর্তন হয়নি তবে এটি সম্পাদনা হিসাবে স্বীকৃত হয়েছিল। কর্মক্ষেত্রে অবহেলিত কিছু ছিল?
টালোনিমি

উত্তর:


304

রানটাইম এপিআই কোডটিতে ত্রুটিগুলি পরীক্ষা করার সম্ভবত সেরা উপায়টি হ'ল স্ট্যান্ডার হ্যান্ডলারের ফাংশন এবং মোড়ক ম্যাক্রোর মতো এটি সংজ্ঞায়িত করা:

#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
inline void gpuAssert(cudaError_t code, const char *file, int line, bool abort=true)
{
   if (code != cudaSuccess) 
   {
      fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
      if (abort) exit(code);
   }
}

তারপরে আপনি gpuErrchkম্যাক্রো দিয়ে প্রতিটি এপিআই কলকে মোড়াতে পারবেন , যা এপিআই কলটিকে মোড়কের মোড়ের অবস্থা থেকে প্রক্রিয়া করবে, উদাহরণস্বরূপ:

gpuErrchk( cudaMalloc(&a_d, size*sizeof(int)) );

যদি কোনও কলটিতে ত্রুটি ঘটে থাকে তবে ত্রুটি বর্ণনা করে এমন একটি পাঠ্য বার্তা এবং আপনার কোডটিতে ফাইল এবং লাইন যেখানে ত্রুটি ঘটেছে তাতে প্রেরণ হবে stderrএবং অ্যাপ্লিকেশনটি প্রস্থান করবে। আপনি প্রয়োজনে আরও পরিশীলিত অ্যাপ্লিকেশন gpuAssertকল করার চেয়ে একটি ব্যতিক্রম বাড়াতে কল্পিত পরিবর্তন করতে exit()পারেন।

একটি দ্বিতীয় সম্পর্কিত প্রশ্ন হ'ল কার্নেল লঞ্চগুলিতে ত্রুটিগুলি কীভাবে পরীক্ষা করা যায়, যা স্ট্যান্ডার্ড রানটাইম এপিআই কলগুলির মতো ম্যাক্রো কলটিতে সরাসরি আবৃত করা যায় না। কার্নেলের জন্য, এরকম কিছু:

kernel<<<1,1>>>(a);
gpuErrchk( cudaPeekAtLastError() );
gpuErrchk( cudaDeviceSynchronize() );

প্রথমে অবৈধ প্রবর্তন যুক্তিটি পরীক্ষা করবে, তারপরে হোস্টকে কর্নেল থামানো এবং একটি সম্পাদন ত্রুটির জন্য পরীক্ষা না করা পর্যন্ত অপেক্ষা করতে বাধ্য করবে। আপনার যদি পরে এর মতো ব্লক করা এপিআই কল থাকে তবে সিঙ্ক্রোনাইজেশনটি নির্মূল করা যাবে:

kernel<<<1,1>>>(a_d);
gpuErrchk( cudaPeekAtLastError() );
gpuErrchk( cudaMemcpy(a_h, a_d, size * sizeof(int), cudaMemcpyDeviceToHost) );

এই ক্ষেত্রে cudaMemcpyকলটি কার্নেল প্রয়োগের সময় ঘটে যাওয়া ত্রুটিগুলি বা মেমরির অনুলিপি থেকে নিজেই ফিরে আসতে পারে। এটি শিক্ষানবিসের জন্য বিভ্রান্তিকর হতে পারে এবং আমি সমস্যা তৈরি হতে পারে তা বুঝতে সহজ করার জন্য ডিবাগিংয়ের সময় কার্নেল লঞ্চের পরে সুস্পষ্ট সিঙ্ক্রোনাইজেশন ব্যবহার করার পরামর্শ দেব।

নোট করুন যে CUDA ডায়নামিক প্যারালালিজম ব্যবহার করার সময় , খুব অনুরূপ পদ্ধতিটি ডিভাইস কার্নেলগুলিতে CUDA রানটাইম এপিআই এর যে কোনও ব্যবহারে প্রয়োগ করা উচিত এবং সেই সাথে কোনও ডিভাইস কার্নেল প্রবর্তন করার পরে:

#include <assert.h>
#define cdpErrchk(ans) { cdpAssert((ans), __FILE__, __LINE__); }
__device__ void cdpAssert(cudaError_t code, const char *file, int line, bool abort=true)
{
   if (code != cudaSuccess)
   {
      printf("GPU kernel assert: %s %s %d\n", cudaGetErrorString(code), file, line);
      if (abort) assert(0);
   }
}

8
@ হারিসম: আমি তা মনে করি না। সম্প্রদায় উইকি প্রশ্ন এবং উত্তরগুলির জন্য যা প্রায়শই সম্পাদিত হয় intended এটি এর মধ্যে একটিও নয়
on

1
আমাদেরও cudaDeviceReset()বেরোনোর ​​আগে যোগ করা উচিত নয় ? এবং স্মৃতি ক্ষয় করার জন্য একটি ধারা?
অরেলিয়াস

2
@ ট্যালোনমিজ: অ্যাসিঙ্ক সিডুএ রানডটাইম কলগুলির জন্য, যেমন চুদামেসেটএেন্সিক এবং চুদা মেম্পপিএসিঙ্ক, এর জন্যও জিপিইউ ডিভাইস এবং হোস্ট থ্রেডকে জিপিইউরচকের (সিডিডিওয়িসিসিনক্রোনাইজ ()) এর মাধ্যমে সিঙ্ক্রোনাইজ করা প্রয়োজন?
নুরভা

2
নোট করুন যে কার্নেল লঞ্চের পরে সুস্পষ্ট সিঙ্ক্রোনাইজেশনটি ভুল নয় তবে কার্যকরভাবে সম্পাদন কর্মক্ষমতা এবং ইন্টারলেভিং শব্দার্থকগুলিকে মারাত্মকভাবে পরিবর্তন করতে পারে। আপনি যদি ইন্টারলিভিং ব্যবহার করে থাকেন তবে ডিবাগিংয়ের জন্য সুস্পষ্ট সিঙ্ক্রোনাইজেশন করা পুরো ক্লাসের বাগগুলি লুকিয়ে রাখতে পারে যা রিলিজ বিল্ডে ট্র্যাক করা শক্ত hard
মাস্টারেক্সিলো

কার্নেল মৃত্যুদন্ড কার্যকর করার জন্য আরও নির্দিষ্ট ত্রুটিগুলি পাওয়ার কি কোনও উপায় আছে? সমস্ত ত্রুটিগুলি আমি পেয়ে যাচ্ছি কেবল আমাকে হোস্ট কোড থেকে লাইন নম্বর দিন, কার্নেল থেকে নয়।
আজমিসভ 25'17

70

উপরের ট্যালোনমির উত্তরটি assertস্টাইল পদ্ধতিতে একটি অ্যাপ্লিকেশন বাতিল করতে একটি দুর্দান্ত উপায় ।

মাঝে মাঝে আমরা বৃহত্তর অ্যাপ্লিকেশনটির অংশ হিসাবে C ++ প্রসঙ্গে ত্রুটি শর্ত থেকে রিপোর্ট এবং পুনরুদ্ধার করতে চাইতে পারি।

এটি std::runtime_errorব্যবহারের থেকে প্রাপ্ত সি ++ ব্যতিক্রম ছুঁড়ে ফেলে এটি করার একটি যুক্তিসঙ্গত সংক্ষিপ্ত উপায় thrust::system_error:

#include <thrust/system_error.h>
#include <thrust/system/cuda/error.h>
#include <sstream>

void throw_on_cuda_error(cudaError_t code, const char *file, int line)
{
  if(code != cudaSuccess)
  {
    std::stringstream ss;
    ss << file << "(" << line << ")";
    std::string file_and_line;
    ss >> file_and_line;
    throw thrust::system_error(code, thrust::cuda_category(), file_and_line);
  }
}

এটি ফাইল নাম, লাইন নম্বর এবং একটি ইংরেজি ভাষার বর্ণনাকে cudaError_tনিক্ষিপ্ত ব্যতিক্রম .what()সদস্যের সাথে অন্তর্ভুক্ত করবে:

#include <iostream>

int main()
{
  try
  {
    // do something crazy
    throw_on_cuda_error(cudaSetDevice(-1), __FILE__, __LINE__);
  }
  catch(thrust::system_error &e)
  {
    std::cerr << "CUDA error after cudaSetDevice: " << e.what() << std::endl;

    // oops, recover
    cudaSetDevice(0);
  }

  return 0;
}

আউটপুট:

$ nvcc exception.cu -run
CUDA error after cudaSetDevice: exception.cu(23): invalid device ordinal

এর ক্লায়েন্ট some_functionসিউডিএর ত্রুটিগুলি অন্য ধরণের ত্রুটিগুলি থেকে আলাদা করতে পারলে পছন্দ করতে পারে:

try
{
  // call some_function which may throw something
  some_function();
}
catch(thrust::system_error &e)
{
  std::cerr << "CUDA error during some_function: " << e.what() << std::endl;
}
catch(std::bad_alloc &e)
{
  std::cerr << "Bad memory allocation during some_function: " << e.what() << std::endl;
}
catch(std::runtime_error &e)
{
  std::cerr << "Runtime error during some_function: " << e.what() << std::endl;
}
catch(...)
{
  std::cerr << "Some other kind of error during some_function" << std::endl;

  // no idea what to do, so just rethrow the exception
  throw;
}

কারণ thrust::system_errorএটি হ'ল std::runtime_error, আমরা যদি পূর্ববর্তী উদাহরণটির যথাযথতা না প্রয়োজন তবে আমরা বিকল্পভাবে ত্রুটিগুলির বিস্তৃত শ্রেণির একই পদ্ধতিতে পরিচালনা করতে পারি:

try
{
  // call some_function which may throw something
  some_function();
}
catch(std::runtime_error &e)
{
  std::cerr << "Runtime error during some_function: " << e.what() << std::endl;
}

1
থ্রাস্ট শিরোনামগুলি পুনরায় সাজানো হয়েছে বলে মনে হচ্ছে। <thrust/system/cuda_error.h>এখন কার্যকরভাবে <thrust/system/cuda/error.h>
chappjc

জ্যারেড, আমি মনে করি আমার মোড়কযুক্ত গ্রন্থাগারটি আপনার প্রস্তাবিত সমাধানগুলিকে সাবমিশন করে - বেশিরভাগ ক্ষেত্রে এবং সম্ভবত প্রতিস্থাপনের পক্ষে যথেষ্ট হালকা। (আমার উত্তর দেখুন)
einpoklum

27

সি ++ - ক্যানোনিকাল উপায়: ত্রুটিগুলির জন্য যাচাই করবেন না ... সি ++ বাইন্ডিংগুলি ব্যবহার করুন যা ব্যতিক্রম ছড়িয়ে দেয়।

আমি এই সমস্যা দ্বারা বিরক্ত হতে ব্যবহৃত; এবং আমি ট্যালোনমিজ এবং জ্যারেডের উত্তরগুলির মতো একটি ম্যাক্রো-কাম-র্যাপার-ফাংশন সলিউশন ব্যবহার করতাম, তবে, সত্যই? এটি CUDA রানটাইম এপিআই ব্যবহার করে আরও কুশ্রী এবং সি-মত করে।

তাই আমি এটি একটি ভিন্ন এবং আরও মৌলিক উপায়ে যোগাযোগ করেছি। ফলাফলের নমুনার জন্য , প্রতিটি রানটাইম এপিআই কলটিতে সম্পূর্ণ ত্রুটি পরীক্ষা করে vectorAdd- এখানে CUDA নমুনার অংশ :

// (... prepare host-side buffers here ...)

auto current_device = cuda::device::current::get();
auto d_A = cuda::memory::device::make_unique<float[]>(current_device, numElements);
auto d_B = cuda::memory::device::make_unique<float[]>(current_device, numElements);
auto d_C = cuda::memory::device::make_unique<float[]>(current_device, numElements);

cuda::memory::copy(d_A.get(), h_A.get(), size);
cuda::memory::copy(d_B.get(), h_B.get(), size);

// (... prepare a launch configuration here... )

cuda::launch(vectorAdd, launch_config,
    d_A.get(), d_B.get(), d_C.get(), numElements
);    
cuda::memory::copy(h_C.get(), d_C.get(), size);

// (... verify results here...)

আবার - সমস্ত সম্ভাব্য ত্রুটিগুলি পরীক্ষা করা হয়েছে, এবং কোনও ত্রুটি ঘটলে একটি ব্যতিক্রম (সতর্কতা: কার্নেলটি প্রবর্তনের পরে যদি কিছু ত্রুটি ঘটায় তবে এটি ফলাফলটি অনুলিপি করার চেষ্টা করার পরে ধরা পড়বে, আগে নয়; কার্নেলটি সফল হয়েছিল তা নিশ্চিত করার জন্য) আরম্ভের সাথে cuda::outstanding_error::ensure_none()কমান্ডের অনুলিপিটির মধ্যে ত্রুটি পরীক্ষা করা দরকার )।

উপরের কোডটি আমার ব্যবহার করে

চুদা রানটাইম এপিআই লাইব্রেরির জন্য গিরি আধুনিক-সি ++ র‌্যাপার (গিথুব)

নোট করুন যে ব্যতিক্রমগুলি ব্যর্থ কলের পরে একটি স্ট্রিং ব্যাখ্যা এবং CUDA রানটাইম এপিআই স্থিতি কোড উভয়ই বহন করে।

এই র‌্যাপারগুলির সাহায্যে সিউডিএ ত্রুটি কীভাবে স্বয়ংক্রিয়ভাবে পরীক্ষা করা হয় তার কয়েকটি লিঙ্ক:


10

সমাধান নিয়ে আলোচনা এখানে আমার জন্য ভাল কাজ করেন। এই সমাধানটি অন্তর্নির্মিত চুদা ফাংশনগুলি ব্যবহার করে এবং এটি প্রয়োগ করা খুব সহজ।

সম্পর্কিত কোডটি নীচে অনুলিপি করা হয়েছে:

#include <stdio.h>
#include <stdlib.h>

__global__ void foo(int *ptr)
{
  *ptr = 7;
}

int main(void)
{
  foo<<<1,1>>>(0);

  // make the host block until the device is finished with foo
  cudaDeviceSynchronize();

  // check for error
  cudaError_t error = cudaGetLastError();
  if(error != cudaSuccess)
  {
    // print the CUDA error message and exit
    printf("CUDA error: %s\n", cudaGetErrorString(error));
    exit(-1);
  }

  return 0;
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.