标题可能听起来很复杂,但在几行代码后很容易理解。
假设您有一个指针数组,可能为NULL或可能指向实际结构。我们的任务是将所有指针设置为NULL(删除无关紧要)。
我们可以通过以下方式实现:
// first way
array[i] = NULL;
// second way
if (array[i] != NULL)
array[i] = NULL;
我想知道,如果我们以第二种方式完成它,我们会在已经为NULL的值上保存一些速度吗?假设数组已经是50%NULL。我的大学讲师曾经提到过“比较并不需要花费太多,而价值在变化”。这是真的吗?如果我们以第二种方式做到这一点,我们会对速度产生任何积极影响吗?或者额外的比较只会浪费时间?
答案 0 :(得分:5)
第一种方式总是更快。你必须读取指针以检查它是否为null,然后写入它,这比编写它需要更长的时间。虽然进行比较本身可能不会花费太多时间,但采取条件分支的后果绝对不是好事。 [好吧,所以编译器可以消除它,但完全没有保证]。
但与性能一样,“在互联网上询问并不能代替测量!”。
答案 1 :(得分:2)
如果我们总能保存一些东西,编译器会在很多情况下进行机械转换。从来没有听说过。
我可以想到至少一个的情况,它可能会保存一些东西:如果你有一个巨大的数组,你在随机位置分配,并且值通常是相同的。在这种情况下,您可能希望花费分支的周期,以避免CPU弄脏高速缓存行并强制将其写回。
答案 2 :(得分:1)
答案是“它取决于”,像往常一样。您的问题集的大小以及特定计算机的特征。没有什么能比得上实际经验证据。
你实际上是在为另一个交易其中一个:
对于每个数组元素,您的第一个解决方案只需要一个内存写入。第二种解决方案需要读取,比较,然后是条件写入。如果读取比写入便宜,并且比较相对便宜,那么如果有很多NULL条目,它可能会更快。
我的头脑回答是第一种方法,相当于memcpy
,在现代处理器上可能更快,特别是如果使用非时间写入进行优化,因为它不会分支(昂贵的!)并且不需要使用仅读取一次的元素来丢弃CPU缓存。
答案 3 :(得分:0)
让我们从基本原则开始:你的导师告诉你的经常是倒退。写入通常比读取快。具体而言(至少对于大多数现代CPU而言)写入仅意味着将地址和值存入队列。然后,CPU的其他部分可以处理将该值写入存储器,而执行单元可以继续执行更多指令。但是,如果在写入队列已满时尝试写入更多数据,则指令流可能会停止。
相比之下,要进行比较,如果数据尚未存在于缓存中,则必须将地址写入内存,然后停止,直到数据从内存到达,以便能够将其用于比较正确。
比较也使用标志寄存器,它具有大量的“寄存器压力”,因为大多数指令都会修改它们。这可以防止本来可用的指令级并行性。
现在,您通常宁愿避免使用相关数据来污染缓存,除非您很快将其用于其他目的。有些缓存完全通过不在写入时分配缓存空间来避免这种情况 - 即,除非您最近读取数据,因此它已经在缓存中,写入它不会将其移动到缓存中;它只是将数据直接写入主存储器。
许多(大多数?)最近的处理器还具有始终直接写入内存的指令,而不管缓存策略如何。英特尔(例如)称这些非临时存储(例如,MOVNTQ和MOVNTPS)。尽管如此,使用它们可能有点棘手。与普通的内存写入不同,默认情况下它们不保证缓存一致性。您需要在写入后执行SFENCE指令,以确保其他处理器将看到写入的结果。
另一方面值得进行比较的是,当单个比较允许您避免写入批次时。例如,让我们假设您的数组非常稀疏,因此只有几百个条目中的一个非空。在这种情况下,您可以(例如)使用位图,其中位图中的一位表示指针是否为空。在这种情况下,单个64位比较可以避免(例如)写入64个不同指针的任何,每个64位。
单独查看指针不会带来优势 - 具体来说,在进行比较之前,您需要将每个指针加载到缓存中,因此尝试逐个比较以避免污染缓存是一个弄巧成拙的主张。