2018年1月8日 星期一

cuda 自動管理記憶體 函式庫

cuda 自動管理記憶體 函式庫

因為還沒有實際在專案上跑過,可能有bug或是考慮不周的情況發生,再自行修改~
另外目前最新版 VS2017 還不支援,有兩個辦法可以處理
  1. 使用VS2015
  2. 安裝舊版VS2017並不更新
  3. 在最新版2017新增2015的套件,讓方案用2015跑
3看起來是最好的,不過實際運行有一些bug,每次編譯完之後更改程式碼在按除錯並不會編譯新的程式碼會直接執行舊的exe,清除重編可以不過每次都按會很麻煩。最佳手段是選1或2。

cuda 通常流程

宣告空間

新建 共享記憶體 這一個cpu跟gpu都可以用,一般都是用這個傳入gpu
T* gpuData;
cudaMalloc((void**)&gpuData, size*sizeof(T));

輸入資料

cudaMemcpy(gpuData, dataIn, size*sizeof(T), cudaMemcpyHostToDevice);

輸出資料

cudaMemcpy(dataIn, gpuData, size*sizeof(T), cudaMemcpyDeviceToHost);

cuda 記憶體控制類別

上述流程繁雜,不過基本上就是跟C語言一樣,為了寫作方便可以讓一個class來管理記憶體,下面是我寫的類別。
稍微有點長我把它放到外部空間gist

使用

基本用法都包含進去,需要什麼在自己包什麼。
下面是使用範例
/*****************************************************************
Name : 
Date : 2018/01/08
By   : CharlotteHonG
Final: 2018/01/08
*****************************************************************/
#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#include <iostream>
#include <vector>
using namespace std;

#include "timer.hpp"
#include "CudaData.cuh"

__global__ void cudacopy(float* b, float* a, int size){
    // 乾式寫法
    const int idx = blockIdx.x*blockDim.x + threadIdx.x;
    if(idx<size){
        b[idx]=a[idx];
    }
    // 迴圈寫法
    //for(int i=threadIdx.x; i<size; i+=blockDim.x){
        //b[i]=a[i];
    //}
}
void cpucopoy(float* b, float* a, int size) {
    for(int i=0; i<size; ++i){
        b[i]=a[i];
    }
}
void testCuda(size_t size) {
    Timer T;
    // 配置主機記憶體
    vector<float> img_data(size), cpu_data(size), gpu_data(size);
    float* a = img_data.data(); // 原始資料
    float* b = cpu_data.data(); // CPU計算後資料
    float* c = gpu_data.data(); // GPU輸出回來資料
    // 設置初值
    float test_val=7;
    for(int i=0; i<size; i++){
        a[i]=test_val;
    }
    // 配置顯示記憶體, 載入資料.
    T.start();
    CudaData<float> gpuDataIn(a, size), gpuDataOut(size);
    gpuDataOut.memset(0, size);
    T.print("  Cuda Data malloc and copy");

    // 網格區塊設定. (與 kernel for 的次數有關)
    const size_t blkDim=16;
    int grid(size/blkDim+1);  // 網格要含蓋所有範圍, 所以除完要加 1.
    int block(blkDim);        // 區塊設定 16x16.
    // Cuda Kernel 執行運算
    T.start();
    cudacopy<<<grid,block>>>(gpuDataOut, gpuDataIn, size);
    T.print("  Cuda-copy");
    // 取出GPU資料
    gpuDataOut.memcpyOut(c, size);

    // CPU 執行運算
    T.start();
    cpucopoy(b, a, size);
    T.print("  Cpu-copy");
    // 測試
    bool f=0;
    for(size_t i = 0; i < size; i++) {
        if(c[i] != b[i]) {f=1;}
    }
    // 測試報告
    if(f==0) {
        cout << "test ok" << endl;
    } else {
        cout << "test Error" << endl;
    }
}
int main(){
    Timer T;
    testCuda(99999);
    T.print("ALL time.");
    return 0;
}

2017年12月31日 星期日

Windows BCD 如何新增 linux 開機選單

Windows BCD 如何新增 linux 開機選單

EasyBCD2.2 用來編輯 BCD 開機選單
iReboot 2.0.1 系統選擇下次啟動使用哪個系統則安裝
以下是操作流程
這樣就好了,再來點進來確認一下有沒有新增
略過開機選單貌似有bug沒辦法實現,可以從這裡下手

其他

linux那邊要新增windows的grub可以安裝軟件
sudo add-apt-repository ppa:danielrichter2007/grub-customizer
sudo apt-get update
sudo apt-get install grub-customizer
這個圖形化介面的摸索一下就可以了
再來如果linux grub壞了,可以用ubuntu的光碟, 在光碟中的系統安裝以下軟件修復
https://help.ubuntu.com/community/Boot-Repair
sudo add-apt-repository ppa:yannubuntu/boot-repair
sudo apt-get update
sudo apt-get install -y boot-repair && boot-repair

sudo add-apt-repository ppa:danielrichter2007/grub-customizer && sudo add-apt-repository ppa:yannubuntu/boot-repair && sudo apt-get update && sudo apt-get install grub-customizer && sudo apt-get install -y boot-repair && boot-repair
開起來會自動引導到修好

簡易指令

上面是比較完整的解決方案,比較簡單的方法是
sudo update-grub
sudo grub-install /dev/sd*
*號你要查一下目標系統在哪裡

2017年12月26日 星期二

Cuda 9.0 範例程式 使用 for 迴圈複製陣列

Cuda 9.0 範例程式 使用 for 迴圈複製陣列

現在9版的安裝 cuda 好像沒有什麼難度了,順對弄對先安裝好 VisualStudio 然後再安裝 cuda 就一路下一步一直按到底就好了。
然後開啟VC就有專案可以開啟了
範例程序有for迴圈可以參考,這裡我把它簡化了一下一些流程並加上時間監控
首先先是計時函式庫
/*****************************************************************
Name : Timer.hpp
Date : 2017/12/19
By   : CharlotteHonG
Final: 2017/12/26
*****************************************************************/
#pragma once
#include <iostream>
#include <string>
#include <ctime>

class Timer {
public:
    Timer(std::string name=""): name(name){
        startTime = clock();
    }
    operator double() {
        return time;
    }
public:
    void start() {
        startTime = clock();
        flag=0;
    }
    void end() {
        finalTime = clock();
        time = (double)(finalTime - startTime)/CLOCKS_PER_SEC;
    }
    void print(std::string name="") {
        if (flag==0) {
            flag = 1;
            end();
        }
        if(name=="") {
            name=this->name;
        }
        if(priSta) {
            std::cout << "#" << name << ", " << " time = " << time << "s" << std::endl;
        }
    }
private:
    std::string name;
    clock_t startTime;
    clock_t finalTime;
    double time;
    bool flag = 0;
public:
    bool priSta = 1;
};
再來是控管 cuda 記憶體的類別,他可以用來自動要求空間並複製進去以及在程序結束時自動解構,不需要再 free() cuda記憶體。
由於這個有寫樣板所以沒法簡單把實作拆開,建議就直接寫在cuh裡面省事吧~
/*****************************************************************
Name : cudaData.cuh
Date : 2017/12/19
By   : CharlotteHonG
Final: 2017/12/19
*****************************************************************/
// Cuda 記憶體自動管理程序
template <class T>
class CudaData {
public:
    CudaData(){}
    CudaData(size_t size){
        malloc(size);
    }
    CudaData(T* dataIn ,size_t size): len(size){
        memcpyInAuto(dataIn, size);
    }
    ~CudaData(){
        if(gpuData!=nullptr) {
            cudaFree(gpuData);
            gpuData = nullptr;
            len = 0;
        }
    }
public:
    void malloc(size_t size) {
        this->~CudaData();
        len = size;
        cudaMalloc((void**)&gpuData, size*sizeof(T));
    }
    void memcpyIn(T* dataIn ,size_t size) {
        if(size > len) {throw out_of_range("memcpyIn input size > curr size.");}
        cudaMemcpy(gpuData, dataIn, size*sizeof(T), cudaMemcpyHostToDevice);
    }
    void memcpyInAuto(T* dataIn ,size_t size) {
        malloc(size);
        memcpyIn(dataIn, size);
    }
    void memcpyOut(T* dataIn ,size_t size) {
        cudaMemcpy(dataIn, gpuData, size*sizeof(T), cudaMemcpyDeviceToHost);
    }
    void memset(int value, size_t size) {
        if(size>len) {
            throw out_of_range("memset input size > curr size.");
        }
        cudaMemset(gpuData, value, size*sizeof(T));
    }
    size_t size() {
        return this->len;
    }
public:
    operator T*() {
        return gpuData;
    }
private:
    T* gpuData;
    size_t len=0;
};

主程式

再來是主程式了,會用到上面兩個函式庫

#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#include <iostream>
#include <vector>
using namespace std;

#include "timer.hpp"
#include "CudaData.cuh"

__global__ void cudacopy(float* b, float* a, int size){
    // 乾式寫法
    const int idx = blockIdx.x*blockDim.x + threadIdx.x;
    if(idx<size){
        b[idx]=a[idx];
    }
    // 迴圈寫法
    for(int i=threadIdx.x; i<size; i+=blockDim.x){
        b[i]=a[i];
    }
}
void cpucopoy(float* b, float* a, int size) {
    for(int i=0; i<size; ++i){
        b[i]=a[i];
    }
}
void testCuda(size_t size) {
    Timer T;

    // 配置主機記憶體
    vector<float> img_data(size), cpu_data(size), gpu_data(size);
    float* a = img_data.data(); // 原始資料
    float* b = cpu_data.data(); // CPU計算後資料
    float* c = gpu_data.data(); // GPU輸出回來資料
    // 設置初值
    float test_val=7;
    for(int i=0; i<size; i++){
        a[i]=test_val;
    }

    // 配置顯示記憶體, 載入資料.
    T.start();
    CudaData<float> gpuDataIn(a, size), gpuDataOut(size);
    gpuDataOut.memset(0, size);
    T.print("  Cuda Data malloc and copy");

    // 網格區塊設定. (與 kernel for 的次數有關)
    const size_t blkDim=16;
    int grid(size/blkDim+1);  // 網格要含蓋所有範圍, 所以除完要加 1.
    int block(blkDim);        // 區塊設定 16x16.

    // Cuda Kernel 執行運算
    T.start();
    cudacopy<<<grid,block>>>(gpuDataOut, gpuDataIn, size);
    T.print("  Cuda-copy");
    // 取出GPU資料
    gpuDataOut.memcpyOut(c, size);

    // CPU 執行運算
    T.start();
    cpucopoy(b, a, size);
    T.print("  Cpu-copy");

    // 測試
    bool f=0;
    for(size_t i = 0; i < size; i++) {
        if(c[i] != b[i]) {f=1;}
    }

    // 測試報告
    if(f==0) {
        cout << "test ok" << endl;
    } else {
        cout << "test Error" << endl;
    }
}

int main(){
    Timer T;
    testCuda(1000);
    T.print("ALL time.");
    return 0;
}

Visual Studio 更改預設 include目錄 不用每次重設

Visual Studio 更改預設 include目錄 不用每次重設

有額外掛其他函式庫的話每次要重建一個新專案,都要重來過更改每一個目錄檔案,這實在是很麻煩痾。
找了一些方法可以永久設置並且對於已經設置好的專案也有效果!
最底下有人懶人包一件安裝,只要把.h檔案放進去桌面產生的捷徑內就好。

設置目錄文件

VisualStudio預設讀取的檔案目錄
%USERPROFILE%\AppData\Local\Microsoft\MSBuild\v4.0
直接貼上就好不用自己改使用者名稱,進來之後可以看到有4個檔案
其中
  • Microsoft.Cpp.Win32.user.props
  • Microsoft.Cpp.x64.user.props
是我們要修改的檔案,就是選擇64位元跟32位元的時候不同的位置
先在這裡新建兩個資料夾分別是(之後做完在改你自己要的名字區分不同程式)
  • include
  • lib
接著先修改 Microsoft.Cpp.x64.user.props 這一份檔案,在 </Project> 前加入下面的字樣
  <!-- UserDfine -->
  <PropertyGroup>
  <IncludePath>$(UserRootDir)\include;$(IncludePath)</IncludePath>
  <LibraryPath>$(UserRootDir)\lib;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(NETFXKitsDir)Lib\um\x64</LibraryPath>
  </PropertyGroup>
修完之後差不多變成這個樣子
再來是 Microsoft.Cpp.x64.user.props 這一份檔案,就是把剛剛有x64的地方改成x86
  <!-- UserDfine -->
  <PropertyGroup>
  <IncludePath>$(UserRootDir)\include;$(IncludePath)</IncludePath>
  <LibraryPath>$(UserRootDir)\lib;$(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);$(NETFXKitsDir)Lib\um\x86</LibraryPath>
  </PropertyGroup>

放入文件

這邊用 gcc 的文件 bits/stdc++.h 做示範,文件可以從這裡下載
https://charlottehong.blogspot.tw/2017/06/visual-studio-2017.html
這個文件的功用是把 C++ 全部的函式庫都載入,這樣妳就不用擔心少載入什麼還要打了;當然這會有一些副作用,不過練習的時候可以少打確實有一些益處,實際開發的專案就不建議這麼做了。
把它丟進來吧

實測

再來需要重啟一下你的VisualStudio,如果你正開著的話。
測試一下開一個全新專案能不能直接引入(舊有已經建立的專案也有效)
/*****************************************************************
Name : 
Date : 2017/12/26
By   : CharlotteHonG
Final: 2017/12/26
*****************************************************************/
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
//================================================================
int main(int argc, char const *argv[]){
    vector<int> chg;
    return 0;
}
//================================================================
有的話就可以瞜~再來自己丟進去就好了
如果需要分類,比如說opencv你想額外建一個資料夾就修改文檔
$(UserRootDir)\include;$(IncludePath)
這裡的 $(UserRootDir) 就是底下這個位置
%USERPROFILE%\AppData\Local\Microsoft\MSBuild\v4.0
用分號隔開表示多個,有第三個就在加一個分號比如說加一個 opencvInc 資料夾
<IncludePath>$(UserRootDir)\opencvInc;$(UserRootDir)\include;$(IncludePath)</IncludePath>

一件安裝懶人包

https://goo.gl/aAHg62
安裝完畢桌面產生一個捷徑就是文中說明的位置,把檔案置放進去就好了。

參考

2017年12月25日 星期一

sublime text3 stino 編譯 arudino 上傳程式碼時錯誤

sublime text3 stino 編譯 arudino 上傳程式碼時錯誤

需要安裝一個套件 stiuno 安裝完畢就可以使用,之後再補詳細過程,今天解決了一個bug先放上來。

紀錄

之前編譯的時候就有遇到這個問題,明明都很正在用在編譯,可是編譯到一半突然就不能用了,重新開機重裝也是一樣,百思不解。
我有上作者的 github 上發問,也沒得到什麼解QuQ
https://github.com/Robot-Will/Stino/issues/436
後來有過了幾個月之後我又去試試看,莫名其妙就自己好了我就沒在管它了。今天在學校重新安裝了一台電腦,這台電腦有重灌過是乾淨的新系統。
沒想到居然彈出一樣的信息,google一下找到自己的文章…..沒有什麼比這個更絕望的你們知道嗎~嗚嗚。

解決辦法

錯誤信息
[Build] C:/Users/Charlotte/OneDrive/Git Repository/BT_ATC...
[Step 1] Check Toolchain.
[Error] Toolchain is not ready. Please build the sketch after the toolchain installation done.
後來我重新開啟 sublmie 他彈出一個視窗在中間上方,要我選擇 UTF8 還有另一個(有時候會彈出這樣的畫面,我也不是很明白是怎麼回事)。
然後我選擇UTF8之後就一切恢復正常了。
有可能是不支持某個格式,之前用 Arduino 編譯器存檔的直接用地三方文本編譯器開有中文會變成亂碼的經驗。
我再用sublimetext也有經驗是編輯到一半突然變成亂碼,不知道是不是我裝了套件影響的,估計是變成其他格式導致stino出問題。
那一篇發問的文章後面有人給出官方的文章
https://playground.arduino.cc/Code/UTF-8
我不確定直接轉是不是有用,你可以試試看,如果你沒有自動彈出。

2017年12月23日 星期六

google雲端硬碟 對資料夾 做副本,複製一份到自己的帳戶

google雲端硬碟 對資料夾 做副本,複製一份到自己的帳戶

對著資料夾建立

在FileURL貼上網址,然後下面選擇自己的雲端空間即可。
(下面中文是直接用google翻譯網頁的)
第一次要授權你的雲端帳號給他用
(建議有重要資料的帳號不要授權,權限開很大)
最後按保存它就會開始複製了
如果沒有複製完全部,可能是副本的容量太大了爆了。
Google雲端對於副本建立,會算入流量限制內,一個帳號一天最多能有100G~150G左右的流量的樣子(詳細多少我沒有去測試,只是依照經驗概算)。
  • 這個就跟分享檔案被太多人下載爆掉一樣,同一個流量。


如何撤銷應用程式的存取權限

進去之後把多餘不要的刪除即可。


第二種方式(舊文)

選擇建立副本
接下來有副本有兩個功能按鈕在上方
第一個是取得你空間的所有權,那個軟體可以自由存取你的雲端硬碟。(我自己也是有點怕怕的,會在意資料隱私或有重要資料夾建議換個帳號)
移除應用程式對帳戶存取權限可以參考下面的網址,如果同時登入多個帳號右上角選一下就可以了。
https://myaccount.google.com/u/0/permissions?pageId=none
接下來請複製你要的連結後面那一長串
貼入這個位置
按下剛剛第一張圖的選項2,就會自動建立整個資料夾副本進去了
不過實際使用上有容量限制,這個是google方的限制,建立副本每日只有100G的限額。