macOS的OpenCL高性能計算

来源:https://www.cnblogs.com/andrewwang/archive/2018/03/23/8633469.html
-Advertisement-
Play Games

隨著深度學習、區塊鏈的發展,人類對計算量的需求越來越高,在傳統的計算模式下,壓榨GPU的計算能力一直是重點。 NV系列的顯卡在這方面走的比較快,CUDA框架已經普及到了高性能計算的各個方面,比如Google的TensorFlow深度學習框架,預設內置了支持CUDA的GPU計算。 AMD(ATI)及其 ...



隨著深度學習、區塊鏈的發展,人類對計算量的需求越來越高,在傳統的計算模式下,壓榨GPU的計算能力一直是重點。
NV系列的顯卡在這方面走的比較快,CUDA框架已經普及到了高性能計算的各個方面,比如Google的TensorFlow深度學習框架,預設內置了支持CUDA的GPU計算。
AMD(ATI)及其它顯卡在這方面似乎一直不夠給力,在CUDA退出後倉促應對,使用了開放式的OPENCL架構,其中對CUDA應當說有不少的模仿。開放架構本來是一件好事,但OPENCL的發展一直不盡人意。而且為了相容更多的顯卡,程式中通用層導致的效率損失一直比較大。而實際上,現在的高性能顯卡其實也就剩下了NV/AMD兩家的競爭,這樣基本沒什麼意義的性能損失不能不說讓人糾結。所以在個人工作站和個人裝機市場,通常的選擇都是NV系列的顯卡。
mac電腦在這方面是比較尷尬的,當前的高端系列是MacPro垃圾桶。至少新款的一體機MacPro量產之前,垃圾桶仍然是mac家性能的扛鼎產品。然而其內置的顯卡就是AMD,只能使用OPENCL通用計算框架了。

下麵是蘋果官方給出的一個OPENCL的入門例子,結構很清晰,展示了使用顯卡進行高性能計算的一般結構,我在註釋中增加了中文的說明,相信可以讓你更容易的上手OPENCL顯卡計算。

//
// File:       hello.c
//
// Abstract:   A simple "Hello World" compute example showing basic usage of OpenCL which
//             calculates the mathematical square (X[i] = pow(X[i],2)) for a buffer of
//             floating point values.
//             
//
// Version:    <1.0>
//
// Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Inc. ("Apple")
//             in consideration of your agreement to the following terms, and your use,
//             installation, modification or redistribution of this Apple software
//             constitutes acceptance of these terms.  If you do not agree with these
//             terms, please do not use, install, modify or redistribute this Apple
//             software.
//
//             In consideration of your agreement to abide by the following terms, and
//             subject to these terms, Apple grants you a personal, non - exclusive
//             license, under Apple's copyrights in this original Apple software ( the
//             "Apple Software" ), to use, reproduce, modify and redistribute the Apple
//             Software, with or without modifications, in source and / or binary forms;
//             provided that if you redistribute the Apple Software in its entirety and
//             without modifications, you must retain this notice and the following text
//             and disclaimers in all such redistributions of the Apple Software. Neither
//             the name, trademarks, service marks or logos of Apple Inc. may be used to
//             endorse or promote products derived from the Apple Software without specific
//             prior written permission from Apple.  Except as expressly stated in this
//             notice, no other rights or licenses, express or implied, are granted by
//             Apple herein, including but not limited to any patent rights that may be
//             infringed by your derivative works or by other works in which the Apple
//             Software may be incorporated.
//
//             The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
//             WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
//             WARRANTIES OF NON - INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A
//             PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION
//             ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
//
//             IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
//             CONSEQUENTIAL DAMAGES ( INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
//             SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
//             INTERRUPTION ) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION
//             AND / OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER
//             UNDER THEORY OF CONTRACT, TORT ( INCLUDING NEGLIGENCE ), STRICT LIABILITY OR
//             OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Copyright ( C ) 2008 Apple Inc. All Rights Reserved.
//
 
////////////////////////////////////////////////////////////////////////////////
 
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <OpenCL/opencl.h>
 
////////////////////////////////////////////////////////////////////////////////
 
// Use a static data size for simplicity
//
#define DATA_SIZE (1024)
 
////////////////////////////////////////////////////////////////////////////////
 
// Simple compute kernel which computes the square of an input array 
// 這是OPENCL用於計算的內核部分源碼,跟C相同的語法格式,通過編譯後將發佈到GPU設備
//(或者將來專用的計算設備)上面去執行。因為顯卡通常有幾十、上百個內核,所以這部分
// 需要設計成可併發的程式邏輯。
// 
const char *KernelSource = "\n" \
"__kernel void square(                                                       \n" \
"   __global float* input,                                              \n" \
"   __global float* output,                                             \n" \
"   const unsigned int count)                                           \n" \
"{                                                                      \n" \
// 併發邏輯主要是在下麵這一行體現的,i的初始值獲取當前內核的id(整數),根據id計算自己的那一小塊任務
"   int i = get_global_id(0);                                           \n" \
"   if(i < count)                                                       \n" \
"       output[i] = input[i] * input[i];                                \n" \
"}                                                                      \n" \
"\n";
 
////////////////////////////////////////////////////////////////////////////////
 
int main(int argc, char** argv)
{
    int err;                            // error code returned from api calls
      
    float data[DATA_SIZE];              // original data set given to device
    float results[DATA_SIZE];           // results returned from device
    unsigned int correct;               // number of correct results returned
 
    size_t global;                      // global domain size for our calculation
    size_t local;                       // local domain size for our calculation
 
    cl_device_id device_id;             // compute device id 
    cl_context context;                 // compute context
    cl_command_queue commands;          // compute command queue
    cl_program program;                 // compute program
    cl_kernel kernel;                   // compute kernel
    
    cl_mem input;                       // device memory used for the input array
    cl_mem output;                      // device memory used for the output array
    
    // Fill our data set with random float values
    //
    int i = 0;
    unsigned int count = DATA_SIZE;
    //隨機產生一組浮點數據,用於給GPU進行計算
    for(i = 0; i < count; i++)
        data[i] = rand() / (float)RAND_MAX;
    
    // Connect to a compute device
    //
    int gpu = 1;
    // 獲取GPU設備,OPENCL的優勢是可以使用CPU進行模擬,當然這種功能只是為了在沒有GPU設備上進行調試
    // 如果上面變數gpu=0的話,則使用CPU模擬
    err = clGetDeviceIDs(NULL, gpu ? CL_DEVICE_TYPE_GPU : CL_DEVICE_TYPE_CPU, 1, &device_id, NULL);
    if (err != CL_SUCCESS)
    {
        printf("Error: Failed to create a device group!\n");
        return EXIT_FAILURE;
    }
  
    // Create a compute context 
    // 建立一個GPU計算的上下文環境,一組上下文環境保存一組相關的狀態、記憶體等資源
    context = clCreateContext(0, 1, &device_id, NULL, NULL, &err);
    if (!context)
    {
        printf("Error: Failed to create a compute context!\n");
        return EXIT_FAILURE;
    }
 
    // Create a command commands
    //使用獲取到的GPU設備和上下文環境監理一個命令隊列,其實就是給GPU的任務隊列
    commands = clCreateCommandQueue(context, device_id, 0, &err);
    if (!commands)
    {
        printf("Error: Failed to create a command commands!\n");
        return EXIT_FAILURE;
    }
 
    // Create the compute program from the source buffer
    //將內核程式的字元串載入到上下文環境
    program = clCreateProgramWithSource(context, 1, (const char **) & KernelSource, NULL, &err);
    if (!program)
    {
        printf("Error: Failed to create compute program!\n");
        return EXIT_FAILURE;
    }
 
    // Build the program executable
    //根據所使用的設備,將程式編譯成目標機器語言代碼,跟通常的編譯類似,
    //內核程式的語法類錯誤信息都會在這裡出現,所以一般儘可能列印完整從而幫助判斷。
    err = clBuildProgram(program, 0, NULL, NULL, NULL, NULL);
    if (err != CL_SUCCESS)
    {
        size_t len;
        char buffer[2048];
 
        printf("Error: Failed to build program executable!\n");
        clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, sizeof(buffer), buffer, &len);
        printf("%s\n", buffer);
        exit(1);
    }
 
    // Create the compute kernel in the program we wish to run
    //使用內核程式的函數名建立一個計算內核
    kernel = clCreateKernel(program, "square", &err);
    if (!kernel || err != CL_SUCCESS)
    {
        printf("Error: Failed to create compute kernel!\n");
        exit(1);
    }
 
    // Create the input and output arrays in device memory for our calculation
    // 建立GPU的輸入緩衝區,註意READ_ONLY是對GPU而言的,這個緩衝區是建立在顯卡顯存中的
    input = clCreateBuffer(context,  CL_MEM_READ_ONLY,  sizeof(float) * count, NULL, NULL);
    // 建立GPU的輸出緩衝區,用於輸出計算結果
    output = clCreateBuffer(context, CL_MEM_WRITE_ONLY, sizeof(float) * count, NULL, NULL);
    if (!input || !output)
    {
        printf("Error: Failed to allocate device memory!\n");
        exit(1);
    }    
    
    // Write our data set into the input array in device memory 
    // 將CPU記憶體中的數據,寫入到GPU顯卡記憶體(內核函數的input部分)
    err = clEnqueueWriteBuffer(commands, input, CL_TRUE, 0, sizeof(float) * count, data, 0, NULL, NULL);
    if (err != CL_SUCCESS)
    {
        printf("Error: Failed to write to source array!\n");
        exit(1);
    }
 
    // Set the arguments to our compute kernel
    // 設定內核函數中的三個參數
    err = 0;
    err  = clSetKernelArg(kernel, 0, sizeof(cl_mem), &input);
    err |= clSetKernelArg(kernel, 1, sizeof(cl_mem), &output);
    err |= clSetKernelArg(kernel, 2, sizeof(unsigned int), &count);
    if (err != CL_SUCCESS)
    {
        printf("Error: Failed to set kernel arguments! %d\n", err);
        exit(1);
    }
 
    // Get the maximum work group size for executing the kernel on the device
    //獲取GPU可用的計算核心數量
    err = clGetKernelWorkGroupInfo(kernel, device_id, CL_KERNEL_WORK_GROUP_SIZE, sizeof(local), &local, NULL);
    if (err != CL_SUCCESS)
    {
        printf("Error: Failed to retrieve kernel work group info! %d\n", err);
        exit(1);
    }
 
    // Execute the kernel over the entire range of our 1d input data set
    // using the maximum number of work group items for this device
    // 這是真正的計算部分,計算啟動的時候採用隊列的方式,因為一般計算任務的數量都會遠遠大於可用的內核數量,
    // 在下麵函數中,local是可用的內核數,global是要計算的數量,OPENCL會自動執行隊列,完成所有的計算
    // 所以在前面強調了,內核程式的設計要考慮、並儘力利用這種併發特征
    global = count;
    err = clEnqueueNDRangeKernel(commands, kernel, 1, NULL, &global, &local, 0, NULL, NULL);
    if (err)
    {
        printf("Error: Failed to execute kernel!\n");
        return EXIT_FAILURE;
    }
 
    // Wait for the command commands to get serviced before reading back results
    // 阻塞直到OPENCL完成所有的計算任務
    clFinish(commands);
 
    // Read back the results from the device to verify the output
    // 從GPU顯存中把計算的結果複製到CPU記憶體
    err = clEnqueueReadBuffer( commands, output, CL_TRUE, 0, sizeof(float) * count, results, 0, NULL, NULL );  
    if (err != CL_SUCCESS)
    {
        printf("Error: Failed to read output array! %d\n", err);
        exit(1);
    }
    
    // Validate our results
    // 下麵是使用CPU計算來驗證OPENCL計算結果是否正確
    correct = 0;
    for(i = 0; i < count; i++)
    {
        if(results[i] == data[i] * data[i])
            correct++;
    }
    
    // Print a brief summary detailing the results
    // 顯示驗證的結果
    printf("Computed '%d/%d' correct values!\n", correct, count);
    
    // Shutdown and cleanup
    // 清理各類對象及關閉OPENCL環境
    clReleaseMemObject(input);
    clReleaseMemObject(output);
    clReleaseProgram(program);
    clReleaseKernel(kernel);
    clReleaseCommandQueue(commands);
    clReleaseContext(context);
 
    return 0;
}

因為使用了mac的OPENCL框架,所以編譯的時候要加上對框架的引用,如下所示:

gcc -o hello hello.c -framework OpenCL

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文主要是在介紹CentOS7.2的環境下Nginx的使用。 ...
  • 操作系統用於管理系統的硬體、軟體和數據資源,控製程序的運行,是應用軟體與硬體之間的介面,也是人機之間的介面。操作系統的職能包括進程管理、存儲管理、文件管理、設備管理、作業管理等。 在進程管理中,PV操作在處理進程的同步與互斥問題方面非常重要,當多個進程需要同時訪問共用資源時會用到。PV是用荷蘭語表示 ...
  • 某個角度上說,kindle很類似android,同樣的Linux內核,同樣的Java用戶層。不過kindle更註重簡單、節能、穩定。Amazon一向認為,功能過多會分散人們閱讀時候的註意力。 Kindle底層的Linux比Android保持了更多的linux相容性,可以使用GTK或者QT編寫程式。Q ...
  • exit(0):正常運行程式並退出程式; exit(1):非正常運行導致退出程式; exit 0 可以告知你的程式的使用者:你的程式是正常結束的。如果 exit 非 0 值,那麼你的程式的使用者通常會認為你的程式產生了一個錯誤。在 shell 中調用完你的程式之後,用 echo $? 命令就可以看到 ...
  • "原文鏈接" 之所以轉載這篇文章,是因為例子舉的太好了,非常適合初學者理解。但是樣式有點亂 自己稍微整理了下。 正向代理 比如你現在缺錢,想找馬雲爸爸去借錢,可想而知人家可能鳥都不鳥你,到最後碰一鼻子灰借不到錢。不過你認識你家隔壁老王,而老王認識馬雲同志,而且關係還很好。這時候你托老王去找馬雲借錢, ...
  • Linux由於其眾多獨特的優勢(可參見Linux系統的優勢),而被很多人所喜愛。而要使用Linux那首先要做的工作就是安裝Linux系統了。這裡給出在 win10 下利用虛擬機 Hyper-v 安裝 Linux 的過程供大家交流學習。 由於 Linux 版本眾多,在選擇時很多人就犯難了,不知道該怎樣 ...
  • 一、shell和shell腳本 在linux系統下,以 #/bin/bash開頭的文本會被shell解釋器進行解釋。 shell是指一種應用程式,這個應用程式提供了一個界面,用戶通過這個界面訪問操作系統內核的服務。 shell腳本(shell script),是一種為shell編寫的腳本程式。業界所 ...
  • 熟悉電腦的人都知道,Linux 相比較於 Windows 有著眾多的優勢,所以現在越來越多的電腦用戶開始使用 Linux 進行辦公、學習。總體來講,Linux 的優勢主要有以下幾個方面。 一、開源、免費 眾所周知,不管是微軟的 Windows 還是蘋果的 macOS,都是需要付費的,而且是比較昂貴的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...