CUDA Texture Part.1 簡介

在執行 CUDA 程式前,都要把資料先從 Host 的記憶體,複製一份到 device 的記憶體中;一般來說,這樣的部分,都是使用 device 的 global memory 來直接進行存取。不過實際上,有的時候還有別的選擇的∼在《nVidia CUDA 簡介》中一文就有提到,除了 global memory 外,還可以透過 constant memorytexture memory 的形式,來對 device memory 資料的存取。

texture 是一般 graphics 裡的名詞,2D texture 大致上可以理解為一張圖片,一般應該是翻譯成材質(維基百科是稱為「紋理」);而由於在傳統的 render pipeline 中,texture 佔了很重要的地位,所以在顯示卡的部分,也會對這部分做特殊的最佳化。而在 nVidia 的 CUDA 中,也把 texture 這項元素保留下來了!

如果在 CUDA 中把 device memory 的資料,當作使用 texture 的話,那資料會變成是唯讀的,要透過特殊的函式來讀取,沒有辦法進行修改;不過相對的,和 global memory 或 constant memory 比起來,也有不少優點∼(詳細資料請參考《CUDA Programming Guide 1.1》的 5.4)

  • 有快取,在某些狀況下頻寬會比較大
  • 不像 global/constant memory 要依照某些存取模式下才有比較好的效能
  • 位置計算的延遲被隱藏得更好,對於隨機存取資料的效能可能更好
  • 被封包過的資料可以在一個運算中被個別的變數使用
  • 8bit 和 16bit 整數可以簡單的轉換成 [0.0, 1.0] 或 [-1.0, 1.0]

除了上述的優點外,如果使用 CUDA Array 來當 texture 的話,更可以套用 filter、使用內建的功能來做內插取值、並且設定要用 clamping 或 repeat 模式處理邊界。

而在 CUDA 中,要使用 texture 的話,是要使用所謂的「texture reference」;下面就大概來介紹 CUDA 中 texture 的使用方法。首先,他的大致流程會是:

  1. Host 的 texture 建立部分
    1. 宣告出 texture reference
    2. 透過 Bind Texture 的函式,將 texture reference 和現有的 device memory 上的變數(linear memory 或 CUDA array)做連結
  2. Device 使用
    1. 透過 CUDA 提供的 texture 讀取函式(tex1Dfetch, tex1D, tex2D)來讀取 texture 的內容
  3. Host 刪除 texture
    1. 呼叫 unbind texture 的函式,將 texture reference 的資源釋放

不過,在 CUDA 中,對於 texture 的控制,有 low-level 和 high level 兩種,Heresy 在這就先針對比較簡單的 high-level 方法來做一些簡單的說明。

CUDA 的 texture 型別

在 CUDA 中,有提供名為 texture 的 template 型別,他的形式是:

texture<Type, Dim, ReadMode> texRef;

其中:

  • Type
    texture 中元素的資料型別;可以是一般的基本的 int, float 型別,也可以是 CUDA 中提供的 vector 型別。
  • Dim
    代表這個 texture 的維度,在 CUDA 中只有 12 兩種值,並不支援 3D texture。
  • ReadMode
    讀取 texture 的模式,有 cudaReadModeNormalizedFloatcudaReadModeElementType 兩種模式。當模式是 cudaReadModeElementType 時,資料會以原來的方式讀取出來;當模式是 cudaReadModeNormalizedFloat、且資料型別是整數型別時,他則會對資料進行 normalize,傳回 [0,1] 或 [-1,1] 之間的數(視原始型別是否為 unsigned 決定)。

舉個例子,如果我們要宣告一個資料型別是 int 的 1D texture,就可以寫成:

texture<int, 1, cudaReadModeElementType> texRef;

texRef 的資料,則還要再透過 BindTexture 的函式,來和 device 上的變數做連結,這樣才算完成 texture 的使用前準備。

不過另外一個要注意的是,目前的 CUDA 似乎只允許將 texture reference 宣告成 global variable,而無法將它宣告在函式內,用參數的方法傳遞給 kernel function(Heresy 這樣寫會使得 nvcc 產生內部錯誤,要寫在 file-scope 是參考 ISI 的課程後才知道的)。

兩種不同的 texture 資料

在 CUDA 中,可以接受兩種資料:linear memoryCUDA array。其中,linear memory 就是之前提過,用 cudaMalloc() 宣告出的連續記憶體空間(一維陣列);而 CUDA array 則是透過 cudaMallocArray(),來宣告出一塊連續的 1D/2D 陣列。

由兩種不同的資料所建立出來的 texture reference,在使用上有一些不同的性質:

Texture with linear memory Texture with CUDA Array
維度 只有一維 一維或二維
讀取的索引 整數 整數或浮點數
filter 不支援 支援 (cudaFilterModeLinear / cudaFilterModePoint)
邊界值 n/a cudaAddressModeClamp / cudaAddressModeWrap
讀取函式 tex1Dfetch() tex1D() / tex2D()

透過 CUDA array 的 texture,可以讓 CUDA 直接幫忙做好內插(雖然目前看來只有線性內插)、邊值處理的問題,其實在很多時候都是相當方便的∼

而由於使用 linear memory 和 CUDA array 的 texture 在使用上有不少的差異,所以接下來就分開寫吧∼

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *