cuda和cudatoolkit

獨孤的大山貓發表於2020-11-28

Pytorch 使用不同版本的 cuda

  由於課題的原因,筆者主要通過 Pytorch 框架進行深度學習相關的學習和實驗。在執行和學習網路上的 Pytorch 應用程式碼的過程中,不少專案會標註作者在執行和實驗時所使用的 Pytorch 和 cuda 版本資訊。由於 Pytorch 和 cuda 版本的更新較快,可能出現程式的編譯和執行需要之前版本的 Pytorch 和 cuda 進行執行環境支援的情況。比如筆者遇到的某個專案中編寫了 CUDAExtension 擴充,而其中使用的 cuda 介面函式在新版本的 cuda 中做了修改,使得直接使用系統上已有的新版本 cuda 時會無法編譯使用。

  為了滿足應用程式和框架本身對不同版本的 cuda 的需求,(如上面遇到的問題中,即需要 Pytorch 能夠切換使用系統上不同版本的 cuda ,進而編譯對應的 CUDAExtension),這裡即記錄筆者瞭解到的 Ubuntu 環境下 Pytorch 在編輯 cpp 和 cuda 擴充時確定所使用 cuda 版本的基本流程以及 Pytorch 使用不同版本的 cuda 進行執行的方法。 

 

cuda 與 cudatoolkit 的區別

  在使用 Anaconda 安裝 Pytorch 深度學習框架時,可以發現 Anaconda 會自動為我們安裝 cudatoolkit,如下圖所示。

  

  上述安裝的 cudatoolkit 與通過 Nvidia 官方提供的 CUDA Toolkit 是不一樣的。具體而言,Nvidia 官方提供的 CUDA Toolkit 是一個完整的工具安裝包,其中提供了 Nvidia 驅動程式、開發 CUDA 程式相關的開發工具包等可供安裝的選項。使用 Nvidia 官網提供的 CUDA Toolkit 可以安裝開發 CUDA 程式所需的工具,包括 CUDA 程式的編譯器、IDE、偵錯程式等,CUDA 程式所對應的各式庫檔案以及它們的標頭檔案。上述 CUDA Toolkit 的具體組成可參考 CUDA Toolkit Major Components.

  實際上,Nvidia 官方提供安裝的 CUDA Toolkit 包含了進行 CUDA 相關程式開發的編譯、除錯等過程相關的所有元件。但對於 Pytorch 之類的深度學習框架而言,其在大多數需要使用 GPU 的情況中只需要使用 CUDA 的動態連結庫支援程式的執行( Pytorch 本身與 CUDA 相關的部分是提前編譯好的 ),就像常見的可執行程式一樣,不需要重新進行編譯過程,只需要其所依賴的動態連結庫存在即可正常執行。故而,Anaconda 在安裝 Pytorch 等會使用到 CUDA 的框架時,會自動為使用者安裝 cudatoolkit,其主要包含應用程式在使用 CUDA 相關的功能時所依賴的動態連結庫。在安裝了 cudatoolkit 後,只要系統上存在與當前的 cudatoolkit 所相容的 Nvidia 驅動,則已經編譯好的 CUDA 相關的程式就可以直接執行,而不需要安裝完整的 Nvidia 官方提供的 CUDA Toolkit .

  通過 Anaconda 安裝的應用程式包位於安裝目錄下的 /pkg 資料夾中,如筆者的目錄即為 /home/xxx/anaconda3/pkgs/ ,使用者可以在其中檢視 conda 安裝的 cudatoolkit 的內容,如下圖所示。可以看到 conda 安裝的 cudatoolkit 中主要包含的是支援已經編譯好的 CUDA 程式執行的相關的動態連結庫。( Ubuntu 環境下 )

  

  在大多數情況下,上述 cudatoolkit 是可以滿足 Pytorch 等框架的使用需求的。但對於一些特殊需求,如需要為 Pytorch 框架新增 CUDA 相關的擴充時( Custom C++ and CUDA Extensions ),需要對編寫的 CUDA 相關的程式進行編譯等操作,則需安裝完整的 Nvidia 官方提供的 CUDA Toolkit.

  本文的後續內容,即對應的是當 Pytorch 等框架需要編譯對應的 CUDA 相關擴充程式時,如何設定使用不同版本的 cuda toolkit( 完整的包含有編譯器的安裝包 )對程式進行編譯,進而滿足特定的 CUDA 版本依賴。

 

Pytorch 確定所使用的 cuda 版本

  實際使用過程中,Pytorch 檢測執行時使用的 cuda 版本的程式碼位於 torch/utils/cpp_extension.py 的_find_cuda_home 函式  ( Pytorch 1.1.0, Line 24 )中.這裡主要介紹 Linux 環境下的 cuda 版本的確認過程,關於 Windows 環境下多版本 cuda 的使用可以參考上述檔案中的具體實現.

  確定 cuda 路徑

  若在執行時需要使用 cuda 進行程式的編譯或其他 cuda 相關的操作,Pytorch 會首先定位一個 cuda 安裝目錄( 來獲取所需的特定版本 cuda 提供的可執行程式、庫檔案和標頭檔案等檔案 )。具體而言,Pytorch 首先嚐試獲取環境變數 CUDA_HOME/CUDA_PATH 的值作為執行時使用的 cuda 目錄。若直接設定了 CUDA_HOME/CUDA_PATH 變數,則 Pytorch 使用 CUDA_HOME/CUDA_PATH 指定的路徑作為執行時使用的 cuda 版本的目錄。

  若上述環境變數不存在,則 Pytorch 會檢查系統是否存在固定路徑 /usr/local/cuda 。預設情況下,系統並不存在對環境變數 CUDA_HOME 設定,故而 Pytorch 執行時預設檢查的是 Linux 環境中固定路徑 /usr/local/cuda 所指向的 cuda 目錄。 /usr/local/cuda 實際上是一個軟連線檔案,當其存在時一般被設定為指向系統中某一個版本的 cuda 資料夾。使用一個固定路徑的軟連結的好處在於,當系統中存在多個安裝的 cuda 版本時,只需要修改上述軟連線實際指向的 cuda 目錄,而不需要修改任何其他的路徑介面,即可方便的通過唯一的路徑使用不同版本的 cuda. 如筆者使用的伺服器中,上述固定的 /usr/local/cuda 路徑即指向一個較老的 cuda-8.0 版本的目錄。

  

  需要注意的是, /usr/local/cuda 並不是一個 Linux 系統上預設存在的路徑,其一般在安裝 cuda 時建立( 為可選項,不強制建立 )。故而 Pytorch 檢測上述路徑時也可能會失敗。   

  若 CUDA_HOME 變數指定的路徑和預設路徑 /usr/local/cuda 均不存在安裝好的 cuda 目錄,則 Pytorch 通過執行命令 which nvcc 來找到一個包含有 nvcc 命令的 cuda 安裝目錄,並將其作為執行時使用的 cuda 版本。具體而言,系統會根據環境變數 PATH 中的目錄去依次搜尋可用的 nvcc 可執行檔案,若環境變數 PATH 中包含多個安裝好的 cuda 版本的可執行檔案目錄( 形如/home/test/cuda-10.1/bin ),則排在 PATH 中的第一個 cuda 的可執行檔案目錄中的 nvcc 命令會被選中,其所對應的路徑被選為 Pytorch 使用的 cuda 路徑。同樣的,若 PATH 中不存在安裝好的 cuda 版本的可執行目錄,則上述過程會失敗,Pytorch 最終會由於找不到可用的 cuda 目錄而無法使用 cuda.比較推薦的做法是保持 PATH 路徑中存在唯一一個對應所需使用的 cuda 版本的可執行目錄的路徑。

  在確定好使用的 cuda 路徑後,基於 cuda 的 Pytorch 擴充即會使用確定好的 cuda 目錄中的可執行檔案( /bin )、標頭檔案( /include )和庫檔案( /lib64 )完成所需的編譯過程。

  

Pytorch 使用特定的 cuda 版本

  從 Pytorch 確定使用的 cuda 版本的流程來看,想要指定 Pytorch 使用的 cuda 版本,主要有兩種方法,第一種是修改軟連結 /usr/local/cuda 所指向的 cuda 安裝目錄( 若不存在則新建 ),第二種是通過設定環境變數 CUDA_HOME 指向所需使用的 cuda 版本的安裝目錄。除此之外,還建議將對應 cuda 目錄中的可執行檔案目錄( 形如/home/test/cuda-10.1/bin )加入環境變數 PATH 中。

  對於第一種方法,由於 /usr/ 和 /usr/local/ 目錄下的檔案均為 root 使用者所管理,故而普通使用者無法對其進行修改。對於具備了 root 許可權的使用者而言,在安裝有多版本 cuda 的 Linux 系統上,只需切換 /usr/local/cuda 所指向的 cuda 目錄,讓其指向所需的 cuda 版本的安裝位置,即可讓 Pytorch 在執行時使用指定版本的 cuda 執行程式。修改軟連結的方法如下命令所示,命令刪除原有的軟連結,並新建指向新路徑的軟連結。

  sudo rm -rf /usr/local/cuda           //刪除軟連結,注意是 /usr/local/cuda 而不是 /usr/local/cuda/,前者僅刪除軟連結,而後者會刪除軟連結所指向的目錄的所有內容,操作請小心
  sudo ln -s cuda_path /usr/local/cuda    //建立名為 /usr/local/cuda 的軟連結,其指向 cuda_path 所指定的 cuda 安裝目錄

  或者直接強制修改原始的軟連結

    sudo ln -sf cuda_path /usr/local/cuda    //修改或建立軟連結 /usr/local/cuda 使其指向指定版本的 cuda 目錄

  對於非 root 使用者而言,主要通過第二種方法進行設定。若想要指定 Pytorch 使用的 cuda 版本,則首先需要設定 CUDA_HOME 環境變數,之後在 PATH 中加入指定 cuda 版本的可執行目錄,也就時 cuda_path/bin/ 目錄。完成設定後,執行 Pytorch 時所使用的即為對應的 cuda 版本。

 

  例項

  以筆者的伺服器賬戶為例,筆者在 /home/test/cuda-10.1 目錄中安裝了 cuda-10.1 ,而伺服器上的 /usr/local/cuda 目錄指向的是之前安裝的老版本的 cuda-8.0,直接執行 Pytorch 時,其會基於上面的確認流程直接使用老版本的 cuda .若想要臨時設定 Pytorch 使用新安裝的 cuda ,則可以通過 export 命令修改全域性變數。這種設定方式在當前終端退出後即失效。

    export CUDA_HOME=/home/test/cuda-10.1/           //設定全域性變數 CUDA_HOME
    export PATH=$PATH:/home/test/cuda-10.1/bin/        //在 PATH 變數中加入需要使用的 cuda 版本的路徑,使得系統可以使用 cuda 提供的可執行檔案,包括 nvcc

  想要永久設定上述 cuda 設定,使用者可以直接在自己的 bash 設定檔案 ~/.bashrc 檔案尾部加入上述命令,儲存後再通過 source ~/.bashrc 執行檔案,即可完成當前終端的環境變數修改。如果需要使用新的 cuda 來編譯檔案,還可以通過 LD_LIBRARY_PATH 變數指定進行連結的 cuda 庫檔案的路徑。

  

  位於 ~/.bashrc 檔案中的指令在每次終端啟動時均會自動執行,後續本使用者所開啟的終端中的環境變數均會首先執行上述檔案中的命令,從而獲得對應的 cuda 變數。

 

其他

  獲取 Pytorch 使用的 cuda 版本

  目前,網路上比較多的資源會討論如何獲得 Pytorch 使用的 cuda 的版本的方法。比較主流的一種方法是使用 Pytorch 提供的方法 torch.version.cuda .

    >>>import torch
    >>>torch.version.cuda    #輸出一個 cuda 版本

  如筆者環境下上述命令的輸出如下圖所示。

  

  事實上,上述輸出的 cuda 的版本並不一定是 Pytorch 在實際系統上執行時使用的 cuda 版本,而是編譯該 Pytorch release 版本時使用的 cuda 版本

  torch.version.cuda 是位於 torch/version.py 中的一個變數, Pytorch 在基於原始碼進行編譯時,通過 tools/setup_helpers/cuda.py 來確定編譯 Pytorch 所使用的 cuda 的安裝目錄和版本號,確定的具體流程與 Pytorch 執行時確定執行時所使用的 cuda 版本的流程較為相似,具體可以見其原始碼( Pytorch 1.1.0, Line 66 ).在進行 Pytorch 原始碼編譯時,根目錄下的 setup.py 會呼叫上述程式碼,確定編譯 Pytorch 所使用的 cuda 目錄和版本號,並使用獲得的資訊修改 torch/version.py 中的 cuda 資訊( Pytorch, Line 286 )。上述 torch.version.cuda 輸出的資訊即為編譯該發行版 Pytorch 時所使用的 cuda 資訊。若系統上的 Pytorch 通過 conda 安裝,使用者也可以直接通過 conda list | grep pytorch 命令檢視安裝的 Pytorch 的部分資訊。   

    conda list | grep pytorch    //檢視安裝的 Pytorch 的資訊

  筆者環境下上述命令的結果如圖所示,可以看到顯示的 cuda 資訊與 torch.version.cuda 保持一致。

  

  想要檢視 Pytorch 實際使用的執行時的 cuda 目錄,可以直接輸出之前介紹的 cpp_extension.py 中的 CUDA_HOME 變數。

    >>> import torch
    >>> import torch.utils
    >>> import torch.utils.cpp_extension
    >>> torch.utils.cpp_extension.CUDA_HOME        #輸出 Pytorch 執行時使用的 cuda