创建一个机器人参加AI迷你杯。 GPU体验


第一条第二条的继续。


下面,我将谈谈作者使用GPU进行计算的经验,包括创建机器人参加AI mini Cup的一部分。 而是,这是有关GPU主题的文章


-你的名字很神奇...
-你知道吗,乔尔?


在童年时代,我们谈论化学尚未开始上学或刚开始发生化学反应的年龄,作者对燃烧的反应着迷,碰巧他的父母没有干扰他,而房子附近的莫斯科荒地偶尔被各种儿童活动的闪光照亮,黑色的自制火箭火药,硝酸糖焦糖等 有两种情况限制了孩子们的幻想:在家用实验室中硝酸甘油的分解,天花板上的酸被嘶嘶作响,开车去警察儿童房,试图从大量散布在Aviamotornaya大都会地区的国防企业中获取化学药品。


然后出现了一家拥有yamaha msx电脑的物理学校,一个可编程的MK计算器在家里,并且没有时间进行化学研究。 孩子的兴趣已经转移到计算机上。 作者从一开始接触计算机就缺乏燃烧的反应,他的程序闷闷不乐,没有这种自然力量的感觉。 您可以看到在游戏中优化计算的过程,但是那时作者不知道如何用该函数的值表替换sin()的计算,因此没有Internet ...


因此,作者能够从计算能力,干净刻录中获得愉悦的感觉,我在计算中使用了GPU。


在habr上有一些关于GPU计算的好文章。 互联网上也有很多例子,因此决定只在周六早上写有关个人感受的文章,这有可能促使其他人走向大规模并行化。


让我们从简单的表格开始。 GPU计算支持多种框架,但最著名的是NVIDIA CUDA和OpenCL。 我们将采用CUDA,立即将我们的编程语言范围缩小到C ++。 有一些库可以使用其他编程语言(例如,C#中的ALEA GPU)连接到CUDA,但这只是另一篇评论文章的主题。


由于他们无法一次制造出带有喷气发动机的大型汽车,尽管其某些指标高于内燃机的指标,所以并行计算并非总是可以应用于实际问题。 并行计算的主要应用程序:您需要一个包含质量特征,多重性元素的任务。 在我们创建机器人的情况下,神经网络(很多神经元,神经连接)处于质量之下,并且机器人群体(计算运动的动态,每个机器人的碰撞都需要花费一些时间),如果机器人的范围是300-1000,那么中央处理器就会投降,并且您会观察到缓慢程序闷烧,例如在可视化帧之间长时间停顿)。


最佳质量选择是,当计算的每个元素不依赖于列表中另一个元素的计算结果时,例如,对数组进行排序的简单任务已经被各种技巧所淹没,因为数组中数字的位置取决于其他数字,并且不能在并行循环中取到前额。 为了简化措辞:成功质量字符的第一个标志是,如果您不需要更改元素在数组中的位置,则可以对其自由执行计算,为此取其他元素的值,但不要将其从其位置移开。 就像童话故事一样:不要更改元素的顺序,否则GPU会变成南瓜。


在现代编程语言中,存在可以在中央处理器或逻辑线程的多个内核上并行执行的结构,并且它们被广泛使用,但是当执行模块的数量超过数百或数千个单元时,作者将读者集中于大规模并行性。


出现平行结构的第一个元素: 平行循环 。 对于大多数任务,就足够了。 从广义上讲,这就是精髓
并行计算。


在CUDA(内核)中编写主循环的示例:


int tid = blockIdx.x * blockDim.x + threadIdx.x; int threadN = gridDim.x * blockDim.x; for (int pos = tid; pos < numElements; pos += threadN) { //    pos,     ,       thread     pos.  :    thread    ,  thread   pos=1146     thread c  pos=956.        .           . } 

关于CUDA文档和评论中已经写了很多内容,涉及GPU块,这些块中产生的线程,如何并行执行它们上的任务。 但是,如果您有一个数据数组,并且显然由质量元素组成,请使用上述循环形式,因为它在视觉上类似于常规循环形式,虽然令人愉快,但不幸的是没有内容。


我认为读者已经了解,与大规模并行编程相比,任务类别正在迅速缩小。 如果我们正在谈论创建游戏,3d 渲染引擎 ,神经网络,视频编辑和其他类似任务,那么阅读器独立行为的清理工作将非常耗时,有大型程序,小型程序,框架,用于这些任务的已知库和未知库。 也就是说,该领域仍然只是主题,以创建您自己的小型计算火箭,而不是SpaceX和Roscosmos,而是一个家庭化的东西,但对计算而言是完全有害的。



这是描绘的完全燃烧火箭的图片。


说到您手中的并行循环将无法解决的任务。 NVIDIA开发人员亲自负责CUDA的创建者已经考虑过这一点。


在某些地方有一个Thrust库很有用,直到“无选项”有所不同为止。 顺便说一句,未找到对Habré的完整评论。


要了解其工作原理,您首先需要对CUDA的原理说三句话。 如果您需要更多单词,可以阅读链接。


CUDA的原则:


计算在GPU上进行,GPU的程序是内核,您必须用C编写。内核又只能与GPU内存进行通信,并且您必须将数据从主程序加载到视频处理器内存中并将其上传回程序中。 CUDA上的复杂算法需要灵活的思维。


因此,Thrust库删除了例程,并为CUDA承担了一些“复杂”任务,例如对数组求和或对其进行排序。 您不再需要编写单独的内核,将指针加载到内存中以及将数据从这些指针复制到GPU内存中。 在主程序中所有的谜团都会发生在您眼前,其速度略逊于CUDA。 Thrust库是用CUDA编写的,因此就性能而言,这是一个浆果领域。


在Thrust中,您需要在其库中创建一个与常规数组(std :: vector)兼容的数组(thrust :: vector)。 那当然不是所有的事情都那么简单,但是作者所说的意思与事实相类似。 实际上有两个数组,一个在GPU(设备)上,另一个在主程序(主机)中。


一个示例将说明语法的简单性(X,Y,Z数组):


 // initialize X to 0,1,2,3, .... thrust::sequence(X.begin(), X.end()); // compute Y = -X thrust::transform(X.begin(), X.end(), Y.begin(), thrust::negate<int>()); // fill Z with twos thrust::fill(Z.begin(), Z.end(), 2); // compute Y = X mod 2 thrust::transform(X.begin(), X.end(), Z.begin(), Y.begin(), thrust::modulus<int>()); // replace all the ones in Y with tens thrust::replace(Y.begin(), Y.end(), 1, 10); 

您可以看到在创建CUDA内核的背景下它看起来是多么无害,并且Thrust中的功能集很大 。 从处理随机变量开始,在CUDA中,它是由单独的cuRAND库(最好由单独的内核运行)完成的,然后根据接近于内核函数的功能对函数进行排序,求和和编写。


作者对使用CUDA和C ++的经验很少,两个月。 关于今年左右的C#。 当然,这与本文开头有关他早期接触计算机,学校物理和应用数学作为教育的观点有些矛盾。 我会这样说。 但是对于我写这篇文章而言,并不是我掌握了所有类似的东西,而是C ++变成了一种舒适的语言(在Habrr类型的“ lambda函数→内部运算符重载”的背景下,我曾经有点怕它)重新定义所有内容“),很明显,经过多年的发展,已经形成了非常友好的开发环境(IDE)。 语言本身是最新版本,似乎从内存中收集垃圾,我不知道它以前是怎么回事。 至少,作者编写的有关最简单算法构造的程序将bot的计算算法驱使了数天,并且在高负载下没有内存泄漏和其他故障。 这也适用于CUDA,乍一看似乎很复杂,但是它基于简单的原理,当然如果有很多GPU的地方很难初始化GPU上的位置,但是随后您将拥有自己的小火箭,视频卡上会冒烟。


在使用GPU进行训练的对象类别中,作者建议使用细胞自动机 。 曾经有一段时间,它们的流行度和时尚度都有所提高,但是神经网络抓住了开发人员的思想。
最多:


“物理学中的每个数量,包括时间和空间,都是有限且离散的。”
而不是细胞自动机。


但是,只有三个简单的公式可以创建此公式,这很美丽:



如果对CUDA上的细胞自动机有所了解,请在评论中写下,其中将有一篇小文章的打字材料。
这是细胞自动机的来源(在视频下方有到这些来源的链接):



早餐后一口气写一篇文章的想法在我看来似乎很可行。 第二次咖啡时间。 周末愉快。

Source: https://habr.com/ru/post/zh-CN417757/


All Articles