从大型矩阵中高效收集(整行)
Efficient gather (of whole rows) from a large matrix
我正在尝试执行一个简单的操作。我有一个大小为 A x B 的矩阵。我有一个长度为 C 的索引列表,我想通过根据索引从第一个矩阵收集行来制作一个 C x B 矩阵。即索引 i 告诉我将第一个矩阵中的哪一行放入第二个矩阵中的第 i 行。
我对索引进行了预排序,因此算法输入平稳:我从 A x B 矩阵加载行并将该行写入 C x B 矩阵中的所有行。
代码看起来像这样:
for(int i = 0;i < A; i ++)
{
for(int k = offsets[i]; k < offsets[i+1]; k ++)
{
int dest = index1[k];
for(int j = 0;j < C/ 8; j++)
{
__m256 a = _mm256_load_ps(&input[i * C + j * 8]);
_mm256_store_ps(&output[dest * C + j * 8] ,a);
}
}
}
代码完全因写入内存而成为瓶颈。
这段代码在 C 很小的时候很有效。然而,当 C 增加时,它的扩展性非常差,我推测这是由于缓存行为。 (与 C = 256 相比,C = 1024 需要 10 倍的时间)。
我尝试在 C 维度中进行阻塞:
for(int c = 0; c < C; c+= K){
for(int i = 0;i < A; i ++)
{
for(int k = offsets[i]; k < offsets[i+1]; k ++)
{
int dest = index1[k];
for(int j = 0;j < C/ 8 / K; j++)
{
__m256 a = _mm256_load_ps(&input[i * C + c + j * 8]);
_mm256_store_ps(&output[dest * C + c + j * 8] ,a);
}
}
}
}
这实际上会使代码变慢。
有什么建议吗?
看来内循环只是流式复制操作。在这种情况下,缓存无关紧要。而是尝试使用简单的 memcpy() 来代替,这样编译器可以产生更好的执行代码,希望如此。
//for(int j = 0;j < C/ 8; j++)
//{
// __m256 a = _mm256_load_ps(&input[i * C + j * 8]);
// _mm256_store_ps(&output[dest * C + j * 8] ,a);
//}
memcpy(&output[dest * C], &input[i * C], C * sizeof(float));
附录
如果得不到满意的结果,不得已,采用C++,将外层循环替换为parllel_for()。那么有可能使缓存(或其他管道?)工作得更好一些。
parallel_for(0, A, [&](const int i) {
for(int k = offsets[i]; k < offsets[i+1]; k++)
{
int dest = index1[k];
memcpy(&output[dest * C], &input[i * C], C * sizeof(float));
}
});
我正在尝试执行一个简单的操作。我有一个大小为 A x B 的矩阵。我有一个长度为 C 的索引列表,我想通过根据索引从第一个矩阵收集行来制作一个 C x B 矩阵。即索引 i 告诉我将第一个矩阵中的哪一行放入第二个矩阵中的第 i 行。
我对索引进行了预排序,因此算法输入平稳:我从 A x B 矩阵加载行并将该行写入 C x B 矩阵中的所有行。
代码看起来像这样:
for(int i = 0;i < A; i ++)
{
for(int k = offsets[i]; k < offsets[i+1]; k ++)
{
int dest = index1[k];
for(int j = 0;j < C/ 8; j++)
{
__m256 a = _mm256_load_ps(&input[i * C + j * 8]);
_mm256_store_ps(&output[dest * C + j * 8] ,a);
}
}
}
代码完全因写入内存而成为瓶颈。 这段代码在 C 很小的时候很有效。然而,当 C 增加时,它的扩展性非常差,我推测这是由于缓存行为。 (与 C = 256 相比,C = 1024 需要 10 倍的时间)。
我尝试在 C 维度中进行阻塞:
for(int c = 0; c < C; c+= K){
for(int i = 0;i < A; i ++)
{
for(int k = offsets[i]; k < offsets[i+1]; k ++)
{
int dest = index1[k];
for(int j = 0;j < C/ 8 / K; j++)
{
__m256 a = _mm256_load_ps(&input[i * C + c + j * 8]);
_mm256_store_ps(&output[dest * C + c + j * 8] ,a);
}
}
}
}
这实际上会使代码变慢。
有什么建议吗?
看来内循环只是流式复制操作。在这种情况下,缓存无关紧要。而是尝试使用简单的 memcpy() 来代替,这样编译器可以产生更好的执行代码,希望如此。
//for(int j = 0;j < C/ 8; j++)
//{
// __m256 a = _mm256_load_ps(&input[i * C + j * 8]);
// _mm256_store_ps(&output[dest * C + j * 8] ,a);
//}
memcpy(&output[dest * C], &input[i * C], C * sizeof(float));
附录
如果得不到满意的结果,不得已,采用C++,将外层循环替换为parllel_for()。那么有可能使缓存(或其他管道?)工作得更好一些。
parallel_for(0, A, [&](const int i) {
for(int k = offsets[i]; k < offsets[i+1]; k++)
{
int dest = index1[k];
memcpy(&output[dest * C], &input[i * C], C * sizeof(float));
}
});