程序设计中多核的影响--多核系列之三

来源:互联网 发布:淘宝助理删除仓库宝贝 编辑:程序博客网 时间:2024/05/20 10:51
1。内存对齐及cache对性能的影响

RISC体系结构下,当数据在自然对齐的边界时内存操作更加高效。CPU在读取内存的时候,是以cache线的长度来读取的,当cache block为128bytes时, CPU每次会读取128bytesL2 cache,如果在分配时不以128bytes对齐就会增加内存的访问次数,对程序的效率有很大的影响。

如果数据超出了cache line的长度(128bytes),确保是以cache block对齐的,这样会保证使用最少的cache block当需要的数据没在cache中,系统将等待直到数据被读入cache。把常用的数据放在一起,可以使用最少的cache block得到需要的数据。因为L1 cache的大小是有限的,如果数据分散在许多cache block中,读不同的数据会造成把仍然需要的数据从cache中清除。

2。循环展开

当循环执行对数组的多重操作时,充分利用并行操作展开部分循环可以提高性能。

char *src, *dst;

int i;

// 32次操作,每次一个字节

for (i = 0; i < 32; i++) {

dst[i] = src[i];

}

以上代码没有采用循环展开,再看如下代码:

uint64_t *src, *dst; // Note instead of char, the type is unit64_t

dst[0] = src[0];

dst[1] = src[1];

dst[2] = src[2];

dst[3] = src[3];

充分利用了CPU64bit长度,每个cycle执行loadstore指令一次,就可以执行64bit数据传递。而在char类型的操作中,每个cycle只能传递8bit

当操作大的数据块时,memset( )memcpy( )执行的效率并不高,其实现都是byte-by-byte,并不能充分利用64bit的特性。这时,就可以改写程序,利用64bit操作并结合cache line 128-byte的特点提高程序效率。

 

3。 Cache 预取

从内存中读取数据到L1L2 cache 中需要几个系统时钟。为了避免读取数据时core的等待,硬件提供了预取指令,能提前将需要的数据读至L1L2 cache使用预取指令主要有两个原因:

Ø  保持core处于busy状态。软件首先执行预取指令,然后进行其它处理。这样core始终处于busy,需要的数据已经提前被读至L1 cache

Ø  优化L2 cache的使用空间。L2 cache 的大小是有限的,多核间需要最优的使用其空间。预取指令可以穿过L2 cache,直接把数据读至L1 cache。节省了L2 cache的空间,也避免了L2 cache 中仍需要的数据被重写。如果cache失效会对性能造成很大的影响。

在代码中适当的位置增加预取指令。最佳位置取决于代码的结构,在预取指令后,代码需要做一些其它工作直到预取完成。最好尽可能早地开始执行预取指令以保证需要时数据已经被读入cache中。

最好的方法是建立一个没有预取指令的原形,然后运行测试以决定在代码的什么位置加入预取会更有好处。有两种情况预取指令特别有用:

Ø  如果数据对象大于一个cache块,在处理之前预取整个数据块。

Ø  当操作一个链表的数据时,在处理一个元素时提前预取下一个元素

 

4。  数据存储相关的优化

1)    写缓冲

core执行storeL2/DRAM指令时,先准备一个写buffer,当数据没在L2 cache中时(发生cache失效),以前的数据从DRAM中要读入cache。所有这些会成为系统的瓶颈。Prepare-For-Store操作用于避免不必要的DRAM读。处理两方面的性能问题:

Ø  实际的写之前建立写buffer

Ø  防止L2 cache失效时读入以前保存的数据。

当写回L2/DRAM的数据保存至每个corewrite buffer。然后发送到L2 cacheL2 cache控制器设置dirty bit,最后写dirty cache 块至DRAM。操作能避免不必要的L2 cache数据已经被设为丢弃但又需要被读取,就需要从DRAM中读回L2 cache。当使用Don’t-Write Back时,core发出命令清除掉cache block dirty bitL2 cache控制器清除掉dirty bit,所以当这个cache block被换出时,就不需要再写回至DRAM

2)    core局部存储

每个core部分的L1 data cache可能被用于保存core的中间结果暂存器。用于保存频繁用到的而并不需要保存到DRAM中的局部变量。由于中间结果暂存器是局部变量,数据并不需要保存至DRAM中,这就减少了write buffer对总线的冲突。

5。spin_lock

对于一段独占某些资源访问的代码,称为临界区,如果多个core要进入临界区,需要使用lock加以保护。每个core进入临界区时,都要检查锁。对于单处理器的多线程常使用信号量来进行互斥。在多核环境里,信号量被实现为spin lockSpin lock导致CPU进入临界区一直等待直到锁被释放。

但是spin lock有以下问题,特别是当有很多个处理器想要获得锁的时候:

Ø  不能使每个核公平地访问临界区,有的核可能会处于饥饿状态。

Ø  不能保证进入临界区的次序,只能是无序地访问。

Ø  当获取锁的时候,多个核只能只能等待,而不能做其它工作。

Ø  每个核获得锁的时间没办法保证,有的核可能一直处于等待状态,很久没能得到锁。

Ø  当核的数目很多时,对于锁的竞争会更激烈,以至于各个核都会很少有机会获得锁。

如何优化spin_lock的性能,也是在程序设计中应该考虑的问题。

阅读(443) | 评论(0) | 转发(0) |
0

上一篇:软件并行模型--多核系列之二

下一篇:QEMU - Running Debian in Debian

相关热门文章
  • Linux串口(serial、uart)驱...
  • C程序设计语言学习--导言(4)...
  • C程序设计语言学习--导言(3)...
  • C程序设计语言学习--导言(2)...
  • 海南三亚政府欲烧毁家私城上亿...
  • linux守护进程的几个关键地方...
  • stagefright与opencore对比
  • 嵌入式Linux之我行——u-boot-...
  • 嵌入式Linux之我行——内核、...
  • android的logcat详细用法
  • ARM常见问题
  • Linux操作系统原理与应用(陈...
  • 简单字符设备驱动程序...
  • 自己学着写lcd入门级驱动程序...
  • Linux2.6.35下s3c64xx/spi子系...
给主人留下些什么吧!~~
原创粉丝点击