The path twists, and the future is uncertain.

分类: Program

高性能计算初步:矩阵乘法

工作需要,最近在学高性能计算相关的知识。
这个领域,不涉足的时候觉得高深莫测,真正接触起来,不仅不神秘,而且十分有趣。
本文基于我对 LAFF-On Programming for High Performance (UTAustinX UT.PHP.16.01x)这门课的学习,结合个人在机器学习方面的经验总结形成,与大家分享。不当之处请指正。

如何实现高性能计算

从字面上来说,算得快就是高性能计算。
以此为定义,拥有超级计算机是做高性能计算的第一步,因为它从硬件性能上支持了「high performance」。
但显然,「硬件快」只是「high performance」的一个组成部分。在有限的硬件性能下,还有方法可以算得快。
例如分布式和并行化:把任务拆分成可以并行执行的多个部分,用多台机器同步运算,是个好主意。
或者给机器上插很多块显卡(GPU),利用显卡的并行计算能力,来做异构计算(由CPU执行串行部分,GPU执行并行部分)。
——如果只有一台机器,没有显卡,机器的性能也很一般呢?

本文接下来要讨论的,就是在常规架构的普通计算机单机上,如何做高性能计算编程。
特殊地,我们讨论最常用的一种运算:矩阵乘法。我们将会看到,它在时下流行的神经网络中如何被广泛应用,又是如何被最高效地实现为C代码。

2021年了,该怎么在Linux(Ubuntu)上部署 CUDA 开发环境?

本文记录于2021年初。不满于网络上的资料大多过时和有瑕疵,重新整理 Ubuntu 环境下配置 CUDA 开发套件的步骤。

  • 本文介绍的步骤主要面向 CUDA 相关的机器学习开发环境配置。所以它包含 CUDA、cuDNN、TensorRT、onnx。如果你不需要其中某些组件,直接跳过即可。
  • 本文涉及的版本信息:Ubuntu 18.04、CUDA 10.2、cuDNN v8.0.5、TensorRT 7.1 GA。请根据你自己的需要选择恰当的版本,唯一注意的是:这几个组件之间的版本必须严格对应,请跟自己的团队确认好版本之后再执行安装,否则会遇到很多版本兼容性问题。

a < b < c 表达式在各种编程语言中的不同「表达」

表妹在大一的C语言课上写了个bug程序,发到群里让大家帮忙debug。我一眼看出其中存在一处“语法”错误:

if (0 < a < 10) {
…
}

一个很有意思的事是,我把它当成了“语法错误”,认为这样写根本编译不过。

但事实是,从之后的讨论中看到,这个程序没有编译失败。只是逻辑上有错而已——这个if后面的表达式永远为true

我用gcc编译了一遍试试,报了这个warning:

warning: comparisons like ‘X<=Y<=Z’ do not have their mathematical meaning [-Wparentheses]
if (0 < a < 10) {

意味着我们一开始的判断是对的,0 < a < 10 确实不是正确的C语言写法,应该写作(0 < a) && (a < 10)。但编译器还是允许它通过了。程序运行时真正发生的是什么呢?:

(0 < a) < 10

相当于先执行了括号内的运算,返回 truefalse。在C语言中 true == 1false == 0。这两个值再去与10做比较。——当然是恒 <10 的,所以 (0 < a < 10) 这个表达式恒为 true,相关 if 语句永远不会走进 else 分支。

这方面的语法差异还挺有趣的,这篇文章里说,如果你没学过C,你可能会以为a < b < c就是a < b < c,如果你学过C,你会以为这里无法编译通过。

关于 a < b < c 的事实是

  • 在 Python 里,a < b < c 的意思就是 a < b < c
  • C语言里,编译能过,但有告警 comparisons like ‘X<=Y<=Z’ do not have their mathematical meaning
  • C++里,表现与C中相同,但还有个额外的告警,警告你在这个表达式中发生了布尔型与整型间的隐式转换
  • Haskell里,这里会发生类型错误,因为 bool 和 int 之间没有隐式转换
  • Fortran里,这是语法错误,因为 < 符号没有关联性(non-associative (meaning operations cannot be chained, often because the output type is incompatible with the input types))。

经验总结

在编译时开启所有编译告警,并尽可能地将它们清零,是一个好的习惯。
当然,也不是一概而论的。取决于你的项目性质,某些告警(未使用的函数、未使用的变量)还是可以选择关闭的。最好是开启所有告警,然后明确声明关闭特定的某几个告警;而不是直接关闭所有告警。

Powered by WordPress & Theme by Anders Norén