今天早上我和同事就这个话题进行了讨论.他说,将数组分配为指针数组总是更好,因为分别分配每个元素有更好的机会获得一个空闲的内存块.有人这样想:
// Consider n_elements as a dynamic value
int n_elements = 10, i;
int **ary = (int **) malloc(sizeof(int *) * n_elements);
for(i = 0; i < n_elements; i++)
{
ary[i] = (int *) malloc(sizeof(int));
}
Run Code Online (Sandbox Code Playgroud)
与他的方法相反,我认为分配元素数组更好,只是因为你会得到一个紧凑的内存块而不是堆在堆中的一堆引用.像这样的东西:
int n_elements = 10;
int *ary = (int *) malloc(sizeof(int) * n_elements);
ary[0] = 100;
Run Code Online (Sandbox Code Playgroud)
在这次谈话之后我一直在思考它,我的最终结论是它取决于它.由于上面提到的原因,我在处理小数据类型时发现第二种解决方案是更好的方法,但是当分配大型结构的数组时,第一种可能更好.
除了我的结论,你怎么看待它?
此处的 GCC 文档指定了 _buitin_prefetch 的用法。
第三个论点是完美的。若为0,编译器产生prefetchtnta(%rax)指令 若为1,编译器产生prefetcht2(%rax)指令 若为2,编译器产生prefetcht1(%rax)指令 若为3(默认),编译器产生prefetcht0 (%rax) 指令。
如果我们改变第三个参数,操作码已经相应地改变了。
但是第二个参数似乎没有任何效果。
__builtin_prefetch(&x,1,2);
__builtin_prefetch(&x,0,2);
__builtin_prefetch(&x,0,1);
__builtin_prefetch(&x,0,0);
Run Code Online (Sandbox Code Playgroud)
以上是生成的示例代码:
以下是组装:
27: 0f 18 10 prefetcht1 (%rax)
2a: 48 8d 45 fc lea -0x4(%rbp),%rax
2e: 0f 18 10 prefetcht1 (%rax)
31: 48 8d 45 fc lea -0x4(%rbp),%rax
35: 0f 18 18 prefetcht2 (%rax)
38: 48 8d 45 fc lea -0x4(%rbp),%rax
3c: 0f 18 00 prefetchnta (%rax)
Run Code Online (Sandbox Code Playgroud)
可以观察到第三个参数的操作码的变化。但即使我更改了第二个参数(指定读或写),汇编代码也保持不变。<27,2a> 和 <2e,31>。所以它不会向机器提供任何信息。那么第二个论点的目的是什么?
这里所述的_mm_prefetch调用将内容从RAM中的给定存储器位置预取到高速缓存行.但是缓存完全在硬件控制之下吗?基于经常访问的存储器(基于空间/时间位置),硬件将内容从存储器预取到高速缓存.我认为程序员无法控制缓存,它完全是一种硬件机制.
所以我的理解是错误的,缓存实际上可以由我们控制,对吧?
如果_mm_prefetch可以控制什么可以放在缓存中,
这是否意味着它永远不会从缓存中删除?
什么是等效的汇编级指令,它适用于缓存机制?
我试图矢量化循环,计算大浮点矢量的点积.我正在并行计算它,利用CPU拥有大量XMM寄存器的事实,如下所示:
__m128* A, B;
__m128 dot0, dot1, dot2, dot3 = _mm_set_ps1(0);
for(size_t i=0; i<1048576;i+=4) {
dot0 = _mm_add_ps( dot0, _mm_mul_ps( A[i+0], B[i+0]);
dot1 = _mm_add_ps( dot1, _mm_mul_ps( A[i+1], B[i+1]);
dot2 = _mm_add_ps( dot2, _mm_mul_ps( A[i+2], B[i+2]);
dot3 = _mm_add_ps( dot3, _mm_mul_ps( A[i+3], B[i+3]);
}
... // add dots, then shuffle/hadd result.
Run Code Online (Sandbox Code Playgroud)
我听说使用预取指令可以帮助加速,因为它可以"在后台"获取更多数据,同时执行muls并添加缓存中的数据.但是我没有找到关于如何使用_mm_prefetch()的示例和解释,何时使用什么地址和什么命中.你可以帮忙吗?
我知道考虑到计算机的工作速度,这听起来可能是一个愚蠢的问题,但是说 RAM 中的某个地址在物理上更靠近主板上的 CPU,与位于尽可能远离 CPU 的内存地址相比,与最远的内存地址相比,这对访问较近的内存地址的速度有影响吗?
众所周知,现代x86_64上的所有缓存L1 / L2 / L3级别都是虚拟索引的,并进行了物理标记。并且所有内核都通过QPI / HyperTransport上的高速缓存一致性协议MOESI / MESIF通过最后一级高速缓存-L3进行通信。
例如,Sandybridge系列CPU具有4至16路高速缓存L3和page_size 4KB,那么这允许在并发进程之间交换数据,并发进程通过共享内存在不同内核上执行。这是可能的,因为高速缓存L3不能同时包含与进程1的页面和与进程2的页面相同的物理内存区域。
这是否意味着每次进程1请求相同的共享内存区域时,进程2会将其页面的缓存行刷新到RAM中,然后进程1加载与页面的缓存行相同的内存区域在process-1的虚拟空间中?真的很慢还是处理器使用了一些优化?
现代的x86_64 CPU是否使用相同的缓存行,而不进行任何刷新,以通过共享内存在具有不同虚拟空间的2个进程之间进行通信?
Sandy Bridge Intel CPU-缓存L3:
低19位-对确定当前设置的数字有效
4 KB-标准页面大小
我们有7个丢失的位[18:12]-即我们需要检查(7 ^ 2 * 16位)= 1024个缓存行。这与1024路缓存相同-因此非常慢。这是否意味着缓存L3(已物理索引,已物理标记)?
标签虚拟地址中丢失位的摘要(页面大小8 KB-12位):
所以我确实在 stackvoerflow 中找到了一些与此相关的答案,但没有一个人清楚地回答了这个
因此,如果我们的内存是字节可寻址的,并且字长是例如 4 字节,那么为什么不使内存字节可寻址呢?
如果我没弄错的话,CPU 会用词对吗?因此,当 cpu 尝试从内存中获取一个字时,从字节可寻址内存中获取 4 字节字与从字可寻址内存中获取一个字之间有什么区别?
我正在开发基于Intel指令集(AVX,FMA等)的高性能算法。当数据按顺序存储时,我的算法(内核)运行良好。但是,现在我面临一个大问题,但没有找到解决方法或解决方案: 请参阅2D矩阵
int x, y; x = y = 4096;
float data[x*y]__attribute__((aligned(32)));
float buffer[y]__attribute__((aligned(32)));
/* simple test data */
for (i = 0; i < x; i++)
for (j = 0; j < y; j++)
data[y*i+j] = y*i+j; // 0,1,2,3...4095, | 4096,4097, ... 8191 |...
/* 1) Extract the columns out of matrix */
__m256i vindex; __m256 vec;
vindex = _mm256_set_epi32(7*y, 6*y, 5*y, 4*y, 3*y, 2*y, y, 0);
for(i = 0; i < x; i+=8)
{
vec = _mm256_i32gather_ps (&data[i*y], …Run Code Online (Sandbox Code Playgroud) 我正在尝试重新实现 malloc 并且我需要了解对齐的目的。据我了解,如果内存对齐,代码将执行得更快,因为处理器不必采取额外的步骤来恢复被切割的内存位。我想我明白 64 位处理器读取 64 位 x 64 位内存。现在,让我们假设我有一个按顺序排列的结构(没有填充):一个字符、一个短字符、一个字符和一个整数。为什么短路会错位?我们拥有区块中的所有数据!为什么它必须在一个 2 的倍数的地址上。对于整数和其他类型,同样的问题?
我还有第二个问题:使用我之前提到的结构,处理器如何知道当它读取它的 64 位时前 8 位对应于一个字符,然后接下来的 16 位对应于一个短等等......?
我试图找到使用 Numba 对两个相同大小的矩阵求和的最快方法。我想出了 3 种不同的方法,但没有一种能打败 Numpy。这是我的代码:
import numpy as np
from numba import njit,vectorize, prange,float64
import timeit
import time
# function 1:
def sum_numpy(A,B):
return A+B
# function 2:
sum_numba_simple= njit(cache=True,fastmath=True) (sum_numpy)
# function 3:
@vectorize([float64(float64, float64)])
def sum_numba_vectorized(A,B):
return A+B
# function 4:
@njit('(float64[:,:],float64[:,:])', cache=True, fastmath=True, parallel=True)
def sum_numba_loop(A,B):
n=A.shape[0]
m=A.shape[1]
C = np.empty((n, m), A.dtype)
for i in prange(n):
for j in prange(m):
C[i,j]=A[i,j]+B[i,j]
return C
#Test the functions with 2 matrices of size 1,000,000x3:
N=1000000
np.random.seed(123)
A=np.random.uniform(low=-10, …Run Code Online (Sandbox Code Playgroud)