通常,计算机记忆被分为不同的性能特征类型(通常称为<>:memory <>/strong>)。 记忆最快的是处理器登记册,可以(通常)在单一锁的周期中查阅和阅读。 然而,这些登记册通常只有少数(通常不超过1KB)。 而另一方面,计算机的主要记忆是巨大的(即8GB),但使用速度要慢得多。 为了提高性能,计算机通常实际安装在上。 这些藏匿处比登记缓慢,但比主要记忆要快得多,因此,如果你能够提供记忆,在藏匿处发现一些东西,则其速度往往比你必须大得多(通常,5-25x之间更快)。 在获得记忆时,处理员首先检查这种价值的记忆,然后将主要记忆重新读到价值中。 如果你在海滩上不断获得价值,那么,如果你恢复记忆,随意获取价值,那么你的工作就会大大提高。
Most programs are written in a way where if a single byte in memory is read into memory, the program later reads multiple different values from around that memory region as well. Consequently, these caches are typically designed so that when you read a single value from memory, a block of memory (usually somewhere between 1KB and 1MB) of values around that single value is also pulled into the cache. That way, if your program reads the nearby values, they re already in the cache and you don t have to go to main memory.
现在,最后一点细节——在C/C++中,阵列储存在滚动顺序上,这意味着在单行矩阵中,所有数值相互储存。 因此,记忆中阵列像第一行,第二行,第三行等。
有鉴于此,请看一下你的法典。 第一个版本就是这样:
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
for (int k = 0; k < n; k++)
c[i][j] = c[i][j] + a[i][k]*b[k][j];
现在,请看一下这一内在的法典。 每一频率的数值都在上升。 这意味着,在操作最便捷的休息室时,在装上<条码>b[k][j]条码>时,该休息室的每艘喷洒都会有误。 其原因是,由于该矩阵储存在输电管上,每当你 in升时,你就重新跳过矩阵的整段,并 jump跃记忆,可能远比你所预示的数值高。 但是,在研究<代码>c[i][j]时,你没有错误(因为i
和j
是相同的),你也不会错过<代码>a[i][k],因为这些数值是按滚动顺序排列的,如果a[i][k]
的价值与以前的编码相挂钩,则该代码的数值从一个相邻的记忆中标明。 因此,每 it一胎,你就会有一孔错。
但审议第二版:
for (int i = 0; i < n; i++)
for (int k = 0; k < n; k++)
for (int j = 0; j < n; j++)
c[i][j] = c[i][j] + a[i][k]*b[k][j];
现在,由于你重新增加了关于每一胎的<条码>j条码>,请思考一下,在最晚的发言中,有多少海滩可能把你ll。 由于这些数值按流体顺序排列,<代码>c[i][j]的价值可能随同,因为从以前的编码中得出的<代码>c[i][j]的价值可能也随附并读。 同样,<条码>b[k][j]可能附在后面,自<条码>i和<条码>>>。 机会是<代码>a[i][k]。 这意味着,每当葬礼时,你可能不会有海滩。
总的说来,这意味着第二版的法典不可能在 lo的每个胎盘上 c,而第一版几乎肯定会发生。 因此,正如你所看到的那样,第二胎的速率可能高于第一胎。
有趣的是,许多汇编者开始获得原型支持,以发现第二版的代码比第一版快。 有些人将试图自动改写法典,以尽量扩大平行。 如果您有。 Purple Ron Book,第11章讨论了这些汇编者如何工作。
此外,你可以进一步利用更为复杂的通道,优化这一通道的运行。 例如,可使用一种称为blocking,通过将阵列分为能够更长时间的次区域,显著提高绩效,然后利用这些区上的多个业务来计算总体结果。
希望这一帮助!