为了增加计算单元的效能,更好地配合计算核心,降低存储器延迟,缓存的概念引入到功能处理器中,例如CPU现在已经拥有L1、L2和L3三个等级缓存,而在GPU中缓存概念还是十分模糊。主要原因是GPU的运算核心数量太多,缓存需求量太大,而另一个方面,在以往的GPU通用计算程序中,确实很少有用到缓存,特别是可读写的真正意义上的缓存。
CPU和GPU功能性单元对比
为了增加GPU的计算能力和计算效率,NVIDIA工程师大胆的将缓存概念引入到GF100中,自然引入缓存势必需要大量晶体管完成,在这点上与CPU道理相同。这样的选择要承担很大风险,但是面向应用设计的GPU必须进行改进,也必须直面问题而不能回避。为了在满足数据计算吞吐率的前提下,NVIDIA工程师为GF100设计了一套实用灵活的L1和L2。
通过了解不同的成千上万的应用程序,NVIDIA工程师发现shared memory可以解决一部分程序的需求,但是不能解决所有的问题。一些应用程序天然需要shared memory,有些应用程序则需要缓存cache,有的既需要shared memory也需要cache。优化的内存设计可以既提供shared memory也提供cache,可以让程序员根据自己的需求来做选择。Fermi架构通过变化存储器的资源配置,可以同时支持这两种需求。
GF100的每一个SM中拥有64KB的可配置片上缓存,可以设置为48KB共享缓存加16KB L1缓存,也可以设置为16KB共享缓存加48KB L1缓存。在之前的GT200核心中,并没有L1缓存的设计。L1缓存可以用于处理寄存器溢出、堆栈操作和全局LD/ST。过去,GPU的寄存器如果发生溢出,会大幅度增加存取时延。
有了L1缓存以后,即使临时寄存器使用量增加,程序的性能表现也不至于大起大落,双精度等运算的衰减控制也将更为优秀。对于那些无法预知数据地址的算法,例如物理计算、光线追踪都可以从GF100的专用L1缓存设计中显著获益。共享缓存的设计则有利于多线程间数据重用,让程序把共享缓存当成缓存来使用,由软件负责实现数据的读写和一致性管理。而对那些没有使用共享缓存的应用程序来说,也可以直接从L1缓存中受益,显著缩减运行CUDA程序的时间。
Fermi有768KB的统一的L2缓存,可以支持所有的存取和纹理操作。L2缓存和所有的SM都相通。L2提供有效和高速的数据支持。有些算法不能在运行前就确定下来,像一些物理问题,光线跟踪,稀疏矩阵乘法,尤其需要缓存的支持。过滤器和转换器需要所有的SM都去读取相同数据的时候,缓存一样会有很大的帮助。
而Fermi的对手代号R800的HD5870所具备的cache是不可随便调用的,HD5870的缓存实际上是传统的Texture cache,只不过现在可以用来临时释放结果做LDS(Local Data Share),不可编程,不可操作,不可写,只读。所以R800现在是16KB LDS+16KB cache,也就是说专用LDS只有16KB。
这里顺便提及Fermi首次在GPU中引入全局ECC的作用。Fermi是第一款支持内存错误检查和修复(ECC)的GPU架构。在使用GPU做大数据量的处理和高性能计算的时候,ECC是有大量的需求。在医疗图像处理和大型集群中,ECC是最有用的特性。
正常情况下的内存位的存储错误,都会引起软件的错误。ECC就是在上述错误没有多系统造成影响的情况下,用来检查和纠正这样的错误。由于这样的错误会根据系统的增大线性的增加,ECC就成为大型集群中必不可少的需求。
Fermi架构GPU的寄存器,共享内存,L1缓存,L2缓存和DRAM内存都受到ECC保护,这样的设计部只是为了高性能的GPU应用,也是为了增加系统的可靠性,这是大规模部署Tesla等高端通用计算产品的前提。但是ECC技术是在原来的数据位上外加位来实现的,所以支持ECC技术的Fermi实现各种存储的代价,都要大于普通GPU。当然我们也找到另外一种说法称FermiDRAM ECC实现机制和传统CPU每8-bit增加一个位元的方式不一样,是一种专利方式。