ARTS(27)

1 Algorithm

不使用 +- 运算符完成两个整数之和计算。

如果不使用 +- 运算符,那么就需要考虑一下两点:

  • CPU 运算单元是完成加法和减法运算;
  • 整数的二进制是怎样表达的;

CPU 运算都是基于二进制的,也就是通过布尔代数完成各种计算。每一个 bit 进行布尔代数计算,除了两数相加各个比特位之间的计算,还包括进位符(carrier) 的特殊处理。布尔代数计算非常简单,只需要绘制出真值表即可。

A B carrier result newCarrier
0 0 0 0 0
0 1 0 1 0
0 1 1 0 1
0 0 1 1 0
1 0 0 1 0
1 0 1 0 1
1 1 0 0 1
1 1 1 1 1

通过的 &|!^ 运算组合就能得到这张真值表的计算公式。
刚刚我们讨论的是加法运算,那么如果整数是负数怎么办? 这就是我们接下来要讨论的话题,在现代计算机中,有符号整数采用 2 的补码 来表示, 它与无符号的整数表示方式是差不多,只不过它的最高位的权重是 $-2^{n-1}$ ($n$ 为整数的长度),如果最高位 0, 那么它就是正整数;反之则为负数。对于一个有符号整数的具体值计算公式:
$$v = -p_n\times 2^{n-1} + p_{n-1} \times 2 ^{n-2} + \ldots + p_2\times 2^1 + p_1 \times 2^0$$
所以对于 32 位整数 -1 二进制表示方式 $\underbrace{11...111}_{32}$,而 32 位整数最大值表示方式 $0\underbrace{11..111}_{31}$。那么为什么采用 2的补码 方式表示有符号整数呢?答案就是就能统一加法和减法(也就是加负数)的运算公式。举一个简单的例子,如果对于 4 表示的有符号整数,采用 2的补码 表示形式分别为 2 = b0010-3 = b1101,采用刚刚讨论的加法计算方法得到 2+(-3) = b1111 = -1 ,因此加法和减法的计算得到统一。

func getSum(a int, b int) int {
    mask := uint32(0x01)
    val := uint32(0x00)
    carrier := uint32(0x00)
    newA, newB := uint32(a), uint32(b)
    for i:= uint(0); i < 32; i++{
        add1 := (newA >> i) & mask
        add2 := (newB >> i) & mask
        digit := add1 ^ add2 ^ carrier
        carrier = (add1 & add2) | (add1 & carrier) | (add2 & carrier)
        val |= digit << i
    }
    return int(int32(val))
}

2 Review

How Graphics Cards Work
显卡是如何工作的
自从 3dfx 首次亮相原先的 Voodoo 加速器,没有任何一个 PC 的配件有如此影响力,它决定了你的机器能否运行这些游戏。尽管其他组件也非常重要,比如顶级的 PC 机: 32GB的内存,500 美元的 CPU 和 PCIe 的存储。但是它仍然在运行现代 3A 游戏的时候仍然力不从心。显卡(通常被称为 GPU)决定了游戏的表现,我们这里将着重讨论它,但是我们通常不会弄清楚为什么 GPU 能够处理这些任务并且是怎么工作的。
如有必要,它将覆盖 GPU 功能的的大的概览,包含了常见的 AMD, Nvidia 和 Intel 集成显卡。它也包含了常见的的移动GPU,比如 Apple, Imagination Technologies, Qualcomm, ARM 和其他厂商。

2.1 为什么我们不使用 CPU 渲染

首先的问题是的为什么我们一开始不使用 CPU 来渲染游戏?老实讲理论上你当然可以直接使用CPU来承担游戏渲染的任务。在 3D 游戏使用显卡渲染之前,像 Utlima Underworld 游戏直接跑在 CPU 上。引用 UU 这个例子有很多理由,首先它比诸如 Doom 游戏有更先进的渲染引擎,完美支持碰撞检测而且还抱哈你了先进的技术比如纹理映射等等。但是这个支持特性包含严重的代价: 几乎没有人拥有能够运行这个程序的CPU。

在 3D 游戏的早期,很多游戏 Half Life 和 Quake II 等都包含了特色是它允许用户关闭 3D 加速器来运行游戏。但是现代游戏已经放弃这个功能,理由也很简单:CPU 被用来设计处理那些通用的的计算,换句话说它们缺乏专门的硬件来提供 GPU 的功能。现在的 CPU 可以流畅的运行 18 年前的软件,但是都无法流畅地运行现代任何一款 3A 游戏,或者说缺乏必要的场景、分辨率和不同的视觉效果。

2.2 什么是 GPU ?

GPU 是一个专门的硬件设备,它能够很好的映射不同的 3D 引擎来执行它们的代码,包括几何创建、纹理映射、内存访问和分区。3D 引擎函数和 GPU 设计者构建的硬件有很大的关联。你们中有人还记得 AMD HD 5000 系列使用了 VLIW5 架构,而更高级别的 HD 6000 系列使用了 VLIW4 架构。通过 GCN, AMD 改变了并行的策略,使得在每个时钟周期表现出更好的性能。

Nvidia 首次在 GeForce 256 上提出了 GPU 这个概念,它能够支持高性能硬件转换和光线计算。直接支持专门硬件计算是早期 GPU 的特色,当然现在这个特色仍然保留,只不过以不同的形式存在,因为比起处理单个数组,处理特定类型的工作负载更加有力和高效。
CPU 和 GPU 的核心有很多不同,但是从高度上来看,你可以认为他们是相同的。CPU 通常设计尽可能的快速高效地执行单线程的代码。SMT 和 超线程等特色就是用来提高这个功能,我们可以通过堆积更多的高效的单线程核心来提高多线程性能。AMD 32核/64线程的 Epyc CPU 是如今你能购买到的最大的 CPU。 从这个角度来讲,Nvidia 最低端的 Pascal GPU 拥有 384 个核心。GPU 的核心相对于 CPU 的的核心而言小得多。
你不能仅仅通过比较 AMD 和 Nvidia 的 GPU 的核心数来确定游戏的性能表现,在同样的 GPU 系列中,GPU拥有更多的核数意味着有更强大的性能表现。
之所以不能简单的通过比较核心数来比较 GPU 的表现原因是不同的架构或多或少有显著的影响。不同于 CPU, GPU 设计用来并行工作的。AMD 和 Nvidia 将他们的显卡划分为不同的计算资源块,Nivida 将这些计算资源块叫做 SM (Stream Multiprocessor),而 AMD 将他们叫做 Compute Unit

每一块都包含一组核心,调度器,注册文件,指令缓存,纹理和 L1 缓存。SM/CU 可以被认为是 GPU 中最小的计算单元。 它并不是包含了所有,比如视频解码引擎,屏幕显示渲染输出、和主板 VRAM 通信的内存接口。但是当 AMD 提出了 APU, 它包含 8 到 11 个 Vega Compute Units,它相当于上面所说的块。如果你查看 GPU 块的示意图,你将会注意到 SM/CU 将会重复很多次。

GPU 中包含更多的 SM/CU, 那么它在每一个时钟周期中并行表现的更好。渲染问题通常被认为是并行难题,这意味着只要核心的数量增加,那么它就往上扩展表现得更好。
当我们讨论 GPU 设计的时候,我们通常都会看到这样格式的 4096:160:64 这样的数字,第一个数字代表着 GPU 核心数,和同一系列比较,这个数字越大 GPU 越快。

2.3 纹理映射和渲染输出

GPU 还有其他两个重要的组成部分: 纹理映射单元和渲染输出。纹理映射单元的数量表明了最大纹理输出和将纹理映射到物理上的最快速度。早期的 3D 游戏使用很少纹理因为绘制 3D 多边形已经足够困难了。纹理并不是 3D 游戏必须的,但是在如今的游戏中没有使用纹理的已经相当少了。
GPU 纹理映射的数量就是 4096:160:64 的第二个数字,AMD,Nvidia 和 Intel 通常将将这个当做 GPU 系列的参考,也就说你不会在同一个 GPU 系列中看到 4096:160:64 配置和 4096:320:64 配置。纹理映射是GPU 在游戏表现的瓶颈,但是下一个 GPU 产品将会提供更高的 GPU 核心和更多的纹理映射单元。
渲染输出将 GPU 的输出和显示器的图像结合起来,渲染输出的速度是 GPU 输出时钟速度和像素填充速度的乘积。更高的 ROP 意味着有更多的像素会被同时被输出,ROP 能够处理模糊、超采样等相关问题。

2.4 内存带宽和内存容量

最后一个要讨论的成分是内存带宽和内存容量。内存带宽指的是每秒钟能有多少数据能够从 GPU 的VRAM 缓存拷贝进出中。很多高级的显示效果要求更多的内存带宽运行在必要的速率上,因为它意味更多的数据从GPU核心中拷贝出入。
在一些场合下,内存带宽的问题是 GPU 可观的性能瓶颈。比如 AMD 的 APU 比如 Ryzen 5 2400G 严重受制于带宽,意味着如你使用 DDR4 内存,那么整体的性能有很大的提高。游戏引擎的选择对 GPU 内存带宽大小有严重的影响,这个也是游戏的重要的目标。
总体的内存容量也是 GPU 重要的影响指标,如果 VRAM 需要的细节和分辨率的图像超出可提供的资源,游戏还能继续运行,但是它需要 CPU 的主存来保留额外的纹理数据。这将会导致 GPU 从 DRAM 中拉取数据而不是从 VRAM 获取数据。这就会导致游戏变得卡起来,因为从 VRAM 中拉取数据比从 DRAM 中快得多。
有一件事必须明白的是,GPU 厂商会有时仅仅是增加 VRAM 大小而不是更新整个产品。我们不能对这个 GPU 是否很有吸引力做出预估因为这个问题取决于 GPU。我能告诉的是你没有必要为仅仅更大的 RAM 显卡而买单。首先要记得是,GPU 的性能被其他东西所限制。如果不信,可以使用 2GB 的显卡和 4GB 的显卡相比较,你可以发现两者的区别并不大, 所以没有必要为这些付钱。

3 Tips

3.1 Shell 定时执行任务

  • watch -n ls -l
  • whie sleep(1); do ls -l; done

3.2 stat 查看文件信息

4 Share

Erlang 语言之父 Joe Armstrong 曾经表达过对 OOP 编程的不满,认为这种编程范式是 suck,下面是他的观点:
反对的理由

  • 数据结构和函数不应该绑定在一起。函数只需要接受输入和输出,而数据结构就是声明。
  • 在面向对象中,每一个实体都必须要是一个对象。
  • 在面向对象中,对象的定义分布在不同的地方,尤其是涉及到对象的继承。
  • 对象包含私有状态,而状态则是万恶之源。

那为什么 OOP 这么流行呢?

  1. 它很容易学习
  2. 它能够让我们的代码复用
  3. 被过度的宣传
  4. 它创建的新的软件产业

对于第 1 点和第 2 点理由是没有任何证据表明,而第 3 点和第 4 点只不过是商业上的需要。
原文
P.S. 上周 Joe Armstrong 去世了,虽然没有学过 Erlang 语言,但是一直在使用由 Erlang 开发的 RabbitMQ 消息队列,致敬)

Comments
Write a Comment