找回密码
 注册
查看: 4955|回复: 20

显卡代替CPU的语言-BrookGpu简介(欢迎增加测试结果

[复制链接]
发表于 2004-6-25 08:36:59 | 显示全部楼层 |阅读模式
相关背景:
关于brookgpu的简要介绍看下面的链接:
http://tech.sina.com.cn/c/2003-12-30/26206.html
本文翻译了斯坦福大学网站上的关于brookgpu语言的一篇文章,原文在:
http://graphics.stanford.edu/projects/brookgpu/lang.html
关于brookgpu的用法在公社中有介绍,请看下面的网址:
http://www.linuxfans.org/nuke/modules.php?name=Site_Downloads&op=geninfo&did=2171
想使用brookgpu需要nvidia的显卡和cg的驱动。


欢迎大家增加测试的数据和你对brookgpu的理解,初次翻译一个新的技术,错误在所难免,请大家多多批评指正!
我的mail:[email protected],欢迎你与我联系!


                 显卡代替CPU的语言-BrookGpu简介
       Brook是标准ANSI C语言的扩展,它是一种为人们所熟悉的高效的语言,它集成了数据并行计算和算术计算集中这两个特点。
通用的计算模型,也就是所谓的流(Stream),同传统的常规语言相比,有以下两个主要的优点:
    1.数据并行:允许编程者指定如何在不同的数据上并行地执行相同的操作。
    2.算术计算集中:鼓励编程者指定数据上的操作,以达到全局的通信最小而局部的计算最大!
      更多的关于Brook的内容可以在Merrimac web site 找到,里面包括了这种语言的完整的说明。Brookgpu为了在GPU上使用,它实现了Brook规范的一个子集。一个Brook 程序中包含合法的C代码和声明流(streams)的句法扩展以及叫做kernels的函数。

流(Streams)
      流是一个新的数据类型的扩展,代表了一系列能够被并行处理的数据。Streams的声明规则和数组很像,都是使用尖括号进行声明的。声明一个流的例子如下所示:
      float s <10, 10>;
      上面声明了一个2维的浮点类型的流。每个流都是由流元素组成的。在上面的例子中,s是一个包含了100个浮点类型流元素的流。流的形状就是指流的维数。在这个例子中,流的形状是10×10。流是用列优先的方式来进行描述的,同C语言中的数组类似。因此,形状为<100>的流与形状为<1,100>和<1,1,100>的流是等同的。
   尽管同C数组很类似,但Brookgpu中的流同C语言中的数组有以下的区别:
1. 在kernels函数外不允许通过下标来获得流元素(例如 s[3][2])。
2. 不允许对流进行静态的初始化,例如:
   float s<100>={1.0f,2.0f,....}是不允许的。
3. 流必须是局部变量(堆栈)。
4. 流只能在核心(kernel)函数中被读写或者是通过特殊的运算符从普通的指针和versa 获得数据。
   streamRead(s,data_s);
    /*用*data_s处的数据对流stream s<>进行赋值*/
    streamWrite(s,data_s);
   /*用流s<>的数据对*data_s进行赋值*/
      尽管这个操作还可能通过编译器进行进一步的优化,但是它向流中进行拷贝已经是十分高效了。
    对于GPU编译过的代码,流对应于组织内存(texture memory)的区域。编译器能进行代码流分析来更好地决定什么时候什么地点来分配组织内存(texture memory),或者完全消除流变量需要暂时存储的需要。为了方便起见,brook也扩展C来包括float2,float3和float4作为基本的数据类型。这些类型同C语言中用typedef 的结构(structs)是等价的。例如:
typedef stuct {
        float x;
        float y;
        float z;
        float w;
} float4;
     另外,这些数据类型能够通过使用构造函数的语法来构造:
float4 a (0.2f,1.0f,3.2f,1.0f);  // x=0.2f,y=1.0f ......
核心(kernels)
      核心是在流上操作的特殊函数,是应用到每个输入流元素的一种并行函数。在一系列的输入流上调用一个核心函数就在每个流元素上实施了隐含的循环,即对每一个流元素调用核心体。通过GPU带有的核心函数,流被输入进视频存储器,核心函数被编译成由其“渲染”的分段程序。除了在声明核心时,它的前面要加上关键字kernel,核心的定义和函数的定义非常的相似,返回类型通常都是void,并且其中的一个流参数要标记类型限定语'out'。全局内存空间和静态变量在kernels里无法被访问。一个kernel声明的例子如下所示:
kernel void k (float s<>,float3 f, float3 f, float a[10][10], out float o<>)
      在这个例子中,输入流s的每一个元素都要调用kernel k的核心体。变量s在kernel体的内部的类型是float,为了每次隐含的调用,s被初始化为一个不同的元素。变量f是kernel中的一个常数,它在每次迭代中保持同一个值。在kernels内部,对输入流或是常数参数进行写操作都是不允许的。
变量a是一个二维数组,它可以通过标准C语言访问数组的方法来访问。a的维数并不需要被指定,然而如果指定了数组的维数,编译器可以更快地产生代码。每一个流都会以数组形式输入到一个核心(kernel)里面。
    变量o是一个输出流。输出流是一个只写的参数,它的值由kernel函数体来进行赋值。在kernel体的内部,变量o的类型也是float。s的每一个元素都隐含地执行kernel体,产生输出流o的元素。注意到kernel的原型和函数体都受限于C和C++支持的cg/HLSL的子集。它们包括向量流类型,矩阵类型和标准的库函数。
  想获得更多的信息,请参照CG语言规范,网址是:
  http://developer.nvidia.com/attach/3722
   也可以参照HLSL文档,网址是:
http://msdn.microsoft.com/library/default.asp?url=/library/en- us/directx9_c/directx/graphics/reference/Shaders/HighLevelShaderLanguage.asp
调用一个kernel函数同调用任何C函数类似。
kernel void k(float s<>, float3 f, float a[10][10], out float o<>);
       float a<100>;
       float b<100>;
       float c<10,10>;
streamRead(a, data1);
       streamRead(b, data2);
       streamRead(c, data3);
   // 调用 kernel "k"
       k (a, 3.2f, c, b);
streamWrite(b, result);

Reductions(约简)
    Brook 提供对流进行并行约简的支持。约简是指将一个流转化为维数更小的流或一个单独的值的操作。这个操作可以由一个单独的,两输入的运算符来定义的,这个运算符必须是可结合的和可交换的。考虑到这个性质,流元素可以被看成是无序的集合,在运算过程中,那些元素可以以任意顺序结合直到产生出约简的结果。
    约简函数只支持那些既能结合又能交换的约简。编译器必须能够以任意的顺序来计算约简。例如,计算一个流的和既是可结合的又是可交换的:
a+b+c+d =(a+b)+(c+d)=a+b+c+d
合法约简的例子是求和、相乘、求最大值或最小值、相或、相与、异或等位操作。不合法约简的例子包括减法和相除。我们必须注意的是编译器并不验证约简函数的合法性。所以编程者必须保证约简操作是合法的。声明一个不合法的约简将会导致进入未定义的执行状态。
约简函数的声明同kernel函数的声明类似,都有一些额外的限制。下面是一个约简函数的例子,它计算了一个浮点流的所有元素的和。
void reduce sum (float a<>,reduce float result<>) {
         result = result + a;
    }
     约简函数只能带有两个流参数,一个输入流和一个输出流,输出流前有关键字reduce,其余的参数都不能是流。另外,两个流的类型必须相吻合。为了计算两个值的约简,kernel要允许对约简参数同时进行读和写。
传递到约简kernel的约简参数可以是标量值或是流。如果约简参数是一个标量,所有的流元素都将调用约简函数来产生一个单独的值,这个值被存放在约简变量中。约简的初始值被定义成输入流的第一个值。

多维约简
    如果约简的参数是一个流,那么如何进行约简操作将是由输入流的维数和约简输出流维数之间的关系来决定的。
   float s<100,200>;

   float t;
   sum(s, t);  // 求s中所有元素的和,结果放在t中
   float t<100, 1>;
   sum(s, t);  

float t<1, 200>;
   sum(s, t);  
   调用约简kernel来约简元素就可以将输入流转换成不同维数的输出流。例如:
   float s<100,200>;
   float t<50, 20>;
   sum(s, t);
在这个例子中,y方向上可以被2整除而x方向上可以被10整除。每生成t的一个元素,s的2×10个元素都要调用执行约简kernel。
如果维数不互相匹配,或是约简的维数比输入流的维数更大,或是输入流的维数不是偶数,都会产生编译的错误。
使用流的形状
   为了生成在当今图形硬件上能有效运行的代码,brook for GPUS并没有实现在官方Brook规范说明中大多数流的运算符
(http://merrimac.stanford.edu/brookspec-v0.2.pdf)。但是,当调用kernels和隐式执行流操作时,Brook这种语言利用了输入流和输出流的相对形状。
    基于流的形状,运算符streamStride和streamRepeat结合在一起并被隐式地调用。例如:

kernel void foo (float a<>, out float b<>);

  float a<10,20>;
  float b<50,10>;

  foo(a,b);
      在这个例子中,输出流b的y方向上比输入流a大5倍,然而输出流b的x方向上只有输入流a的一半大。对于b中的每一个流元素,kernel foo只被调用一次。流a隐式地改变大小来匹配输出流的大小。在这个例子中,在y方向上通过重复元素的方式(1,1,1,1,1,2,2,2,2,2,3,3,3,3,3....)来使维数增加,而通过在x方向上每隔一个去掉一个元素的方法(1,3,5,7,9....)来使维数减少。每个输入流都重复上面的操作。数组参数不受影响。

重复流
   重复流是一种特殊类型的流,它预先就被初始化为一系列顺序的值(1,2,3,4,...)。创建一个重复流使用iter关键字和运算符:
   iter float s<100> = iter(0.0f, 100.0f);
  // s被初始化成0.0, 1.0, 2.0, ..., 99.0
    iter操作符的第一个参数通常是流的初始值。第二个操作符是流的数值的上限,产生完全相同的形式【初始值,上限】。流元素之间的步长等于(初始值-上限)/流中元素的个数。
iter float s<100> = iter(0.0, 1.0f);
// s: 0.00, 0.01, 0.02, 0.03, ..., 0.99

iter float s<10> = iter(2.0f, 7.0f);
// s: 2.0, 2.5, 3.0, 3.5, ..., 6.5
    一维重复流也同时与float2,float3,float4类型一起使用。每一个元素都被单独的插值。

iter float2 s<10> = iter(float2(0, 0), float2(10, 5));
// s: (0,0), (1,0.5), (2,1), ..., (9,4.5)。
    目前的编译器只支持一维和二维的重复流。如果流是二维的,流元素的类型必须是float2。在二维的情况下,插值稍稍有些不同。
   
   iter float2 s<4, 4> = iter (float2(0,0), float2(4, 10))
  s:
   (0,   0)  (1,   0)  (2,   0) (3,   0)
   (0, 2.5)  (1, 2.5)  (2, 2.5) (3, 2.5)
   (0,   5)  (1,   5)  (2,   5) (3,   5)
   (0, 7.5)  (1, 7.5)  (2, 7.5) (3, 7.5)
      流元素的每一个部分都会根据相应维数的最大和最小值进行插值。
目前Brookgpu编译器和运行不支持二维以上的重复流,更进一步地说,如果一个重复流传递进入一个kernel,它的维数必须和输出流的维数相匹配。这些限制可能会在将来发行的版本中取消,目前重复流和数据流并不相同,因此,重复流的参数中必须包含iter这个关键字,而且将重复流做为一个输出结果传进kernel也是不允许的。这些限制可能会在将来发行的版本中改变。
ScatterOp
streamScatterOp(s, index_stream, array,STREAM_SCATTER_ASSIGN);

StreamScatterop运算符执行一个间接的写操作,流s中包含传递给Scatter的数据,索引流(index Stream)中包含在数组中要写入数据的偏移量。第四个参数是执行如下的操作:将新来的数据同已经保存在数组中的数据结合起来,这个参数可以是一个约简函数,或是枚举函数的一个组成部分,例如STREAM_SCATTER_ASSIGN执行一个将流数据直接写入数组的写操作。

GatherOp
streamGatherOp(t, index_stream, array, STREAM_GATHER_FETCH);

streamGatherOp执行一个数组上间接的读操作。它通过使用索引流来生成一个取值的流(t)。如果数组是多维的,索引流提供一个在数组内的线性偏移(基于C语言数组的列优先排列)。
    第四个参数可以是任何kernel函数,这个函数包含一个单独的输出流或是一个预定义的操作符。STREAM_GATHER_FETCH参数指出Gather操作仅仅读取值并将这个值放入流中就行了。
发表于 2004-6-25 09:55:18 | 显示全部楼层
up up

thanks for your efforts
回复

使用道具 举报

发表于 2004-6-25 09:59:09 | 显示全部楼层
看上去不支持 linux 的说

有 linux 下的编译器吗? linux 下的 nvidia 显卡驱动支持吗?
回复

使用道具 举报

发表于 2004-6-25 10:43:35 | 显示全部楼层
这是linux下的!
http://www.nvidia.com/object/linux.html
并且linux下nv和ATI9200以上都可以
http://www.linuxfans.org/nuke/modules.php?name=Site_Downloads&op=vote&did=2475&vote=good
回复

使用道具 举报

发表于 2004-6-25 10:47:10 | 显示全部楼层
这是 driver ,但是不知道这个是不是支持运行这种语言编译出来的程序...
回复

使用道具 举报

发表于 2004-6-25 12:34:49 | 显示全部楼层
偶在linux下用过,没问题,楼主也是再linux下搞的把。。。brcc 用于把.br可以转为.cpp,再用GCC或ICC编译可以出ELF,设
export BRT_RUNTIME=cpu(或者nv30gl)就可以了
回复

使用道具 举报

 楼主| 发表于 2004-6-25 13:04:16 | 显示全部楼层
在windows下和linux下都是可以使用的,具体对硬件的要求可以在brookgpu的主页或是brook软件包中的readme中找到!我在linux下用过的,软件包中的一部分测试程序是正确的!而有些是针对别的显卡驱动写的,运算结果不对的!
回复

使用道具 举报

发表于 2004-6-25 14:00:19 | 显示全部楼层
利用显卡来运行普通的C程序?这样做的意义是什么?是和主板上的CPU进行并行计算吗?
回复

使用道具 举报

发表于 2004-6-25 14:07:10 | 显示全部楼层
利用显卡来运行普通的C程序,这样MP3或占CPU高的程序就可高速运行了
回复

使用道具 举报

发表于 2004-6-25 14:10:04 | 显示全部楼层
[quote:e3f3e6e839="PXE2"]利用显卡来运行普通的C程序,这样MP3或占CPU高的程序就可高速运行了[/quote]

你的意思是利用显卡来运行MP3或其他占CPU高的程序?譬如电影文件重新编码这些也行?
回复

使用道具 举报

发表于 2004-6-25 15:28:23 | 显示全部楼层
是阿。。就是做个FPU其实如果用来解rmvb不是可以大大降低cpu的占用吗?
回复

使用道具 举报

发表于 2004-6-25 18:49:28 | 显示全部楼层
就是充分利用系统里的各种资源嘛~
回复

使用道具 举报

发表于 2004-6-29 10:15:44 | 显示全部楼层
Cg Toolkit - Linux





This document contains instructions for downloading and using the Cg Toolkit under Linux.



Table of Contents



Requirements

Downloading

Compiling

Running

Downloading

Building the Toolkit

Enabling NV30 Emulation

Frequently Asked Questions (FAQ)

Downloading the Toolkit

Compiling/Running the Demos





Requirements



Downloading



To download the Cg Toolkit you'll need a internet connection and CVS and SSH2 installed on your machine.



Compiling



You'll need gcc 2.96 or better (gcc 3.0+ is recommended and comes preinstalled on many new distributions of Linux such as RedHat 8.0) to compile the various libraries and demos that are included with the SDK. All of the demos use GLUT so you'll need to make sure it's installed and working before attempting to compile or run any of the demos.



To compile any of the Cg demos, you'll need to install the Cg compiler.



Running



To get the most out of this toolkit its recommended that you have a GeForce3 or better with the latest Detonator 40 drivers installed. Some effects will run on GeForce1/2 level hardware but the majority will require a GeForce3 or better GPU. A few of the demos will only run on a GeForce FX or on a GeForce 3/GeForce 4 with NV30 emulation enabled.



Downloading

The CVS repository is accessible under Linux using standard CVS and SSH binaries. If you do not have these installed please visit www.cvshome.org (CVS) and www.openssh.com (SSH) and download the binaries for your particular distribution. Please note that SSH 2 is required to access the CVS repository.

Once CVS and SSH are installed, follow these steps to download the toolkit from the CVS server.



1. Go to your home directory.

cd

2. Create a '.ssh' folder and place the CVS identity file in it.

mkdir .ssh

cd .ssh

wget http://developer.nvidia.com/object/NVSDK_CVS_SSH_Key.html

chmod 600 nvcvsident

cd ..

3. Create a SSH script for accessing the CVS server using the anoncvs user. Paste the following into a 'sshanoncvs' file that's stored in your home directory.

#!/bin/sh
chmod 600 $0
ssh -q -C -i ~/.ssh/nvcvsident $@
chmod 755 $0
exit

4. Make the 'sshanoncvs' script executable.

chmod +x sshanoncvs

5. Set the CVS_RSH environment variable to point to the script just created.

export CVS_RSH=~/sshanoncvs

6. Set the CVSROOT environment variable to store the CVS server, connection method, user, and repository location.

export CVSROOT=:ext:[email protected]:/repos



NOTE: You may want to set these environment variables in your profile settings (typically ~/.bash_profile or ~/.profile) so that you do not have to set them every time you want to update the toolkit.

7. Download the toolkit (this step will take a while as the amount of data getting downloaded is ~200MB's uncompressed).

cvs -z3 co SDK

Once this step is done you'll have a copy of the Cg Toolkit installed and you just have to build it. If you need to update the toolkit, execute the following command from the root directory of the toolkit:



cvs -z3 up -d -P .



which will update y
回复

使用道具 举报

 楼主| 发表于 2004-6-29 21:56:37 | 显示全部楼层
下面是一个小的实例程序,其实很简单的!用kernel(核心函数)求一维和二维输入流的和!     
#include <stdio.h>

kernel void sum( float a<>, float b<>, out float c<> ) {
        c = a + b;
}

int main() {
        int i, j;
        float a1< 100 >;
        float b1< 100 >;
        float c1< 100 >;
        float a2< 10, 10 >;
        float b2< 10, 10 >;
        float c2< 10, 10 >;
        float input_a1[100];
        float input_b1[100];
        float output_c1[100];
        float input_a2[10][10];
        float input_b2[10][10];
        float output_c2[10][10];
       
        // initialize data
        for( i = 0; i < 10; i++ )
        {
                for( j = 0; j < 10; j++ )
                {
                        input_a1[10*i+j] = (float)i;
                        input_b1[10*i+j] = (float)10*j;
                        input_a2[j] = (float)i;
                        input_b2[j] = (float)10*j;
                }
        }
        streamRead( a1, input_a1 );
        streamRead( b1, input_b1 );
        streamRead( a2, input_a1 );
        streamRead( b2, input_b2 );
       
        sum( a1, b1, c1 );
        sum( a2, b2, c2 );
       
        streamWrite( c1, output_c1 );
        streamWrite( c2, output_c2 );
       
        for( i = 0; i < 10; i++ )
        {
                for( j = 0; j < 10; j++ )
                        printf( "%3.2f ", output_c1[10*i+j] );
                printf("\n");
        }
       
        for( i = 0; i < 10; i++ )
        {
                for( j = 0; j < 10; j++ )
                        printf( "%3.2f ", output_c2[j] );
                printf("\n");
        }
       
        return 0;
}
回复

使用道具 举报

发表于 2004-7-1 06:46:31 | 显示全部楼层
我在 Windows 下编译成功用的是 MingW, 测试 CPU Runtime 也可以。
只是显卡不行才 Geforce 4 MX,没有 Pixel Shader 不能测试~~ :-(
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

GMT+8, 2025-2-6 19:28 , Processed in 0.028025 second(s), 15 queries .

© 2001-2025 Discuz! Team. Powered by Discuz! X3.5.

快速回复 返回顶部 返回列表