- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我已经实现了一个简单的 n x n 矩阵乘法,以使用 OpenMp 在 c 中测试相同的性能调整。我的初始代码如下:
#pragma omp parallel for shared(a,b,c) private(h,i,j,k)
for( i = 0; i < n; i++ )
{
for( j = 0; j < n; j++)
{
for( k = 0; k < n; k++ )
{
a[i*n+j]+= b[i*n+k] * c[k*n+j];
编译器切换 j 和 k 循环,所以我得到了 ikj 算法。我想要实现的第一件事是填充以将每一行对齐到 64 字节以进行对齐的缓存访问。因此,我计算了每一行所需的额外大小。行长度为 5900,新尺寸为 5904(引用号为 ISBN-9780124104143)。我的新代码如下:
#pragma omp parallel for shared(a,b,c) private(h,i,j,k)
for( i = 0; i < n; i++ )
{
for( k = 0; k < n; k++ )
{
#pragma simd
#pragma unroll(4)
for( j = 0; j < n; j++)
{
a[i*pn+j]+= b[i*pn+k] * c[k*pn+j];
其中 pn 是新的填充行长度。我不得不手动置换我的循环,因为编译器拒绝这样做。在普通的 Xeon 处理器上运行这段代码,我得到的性能几乎与以前相同,也许更好一点。这就是我所期望的。但是当我在 Xeon Phi 上运行代码时,它大约是初始代码的 1/10。
经过进一步的编译器调查,我注意到,内部循环不再展开和向量化。所以我添加了以下#pragmas:
#pragma simd
#pragma unroll
矢量化工作正常,但余数循环没有展开。性能要好得多,但仍然只有普通版本的 1/2 左右。
这是正常代码的编译器 (-O3) 输出:
LOOP BEGIN at mm_par.c(75,3)
remark #25444: Loopnest Interchanged: ( 1 2 3 ) --> ( 1 3 2 )
LOOP BEGIN at mm_par.c(79,5)
remark #25460: No loop optimizations reported
LOOP BEGIN at mm_par.c(77,4)
remark #15301: PERMUTED LOOP WAS VECTORIZED
LOOP END
LOOP BEGIN at mm_par.c(77,4)
<Remainder>
remark #25436: completely unrolled by 4
LOOP END
LOOP END
LOOP END
这里是带有 simd 和展开编译指示的填充输出:
LOOP BEGIN at mm_ali.c(76,3)
remark #25460: No loop optimizations reported
LOOP BEGIN at mm_ali.c(78,4)
remark #25460: No loop optimizations reported
LOOP BEGIN at mm_ali.c(82,10)
remark #15301: SIMD LOOP WAS VECTORIZED
LOOP END
LOOP END
LOOP END
所以展开被忽略了。有没有办法强制它?我也质疑自己,如果那是表现不佳的唯一原因..
编辑:没有填充的快速矩阵乘法的汇编如下所示:
vmovapd c(%r15,%rbx,8), %zmm1 #81.28 c1
vprefetche1 2048+a(%r11,%rbx,8) #81.6 c5
vmovapd 64+c(%r15,%rbx,8), %zmm3 #81.28 c9
vprefetch0 768+a(%r11,%rbx,8) #81.6 c13
vmovapd 128+c(%r15,%rbx,8), %zmm4 #81.28 c17
vprefetch1 2048+c(%r15,%rbx,8) #81.28 c21
vmovapd 192+c(%r15,%rbx,8), %zmm5 #81.28 c25
vprefetch0 768+c(%r15,%rbx,8) #81.28 c29
vfmadd213pd a(%r11,%rbx,8), %zmm0, %zmm1 #81.6 c33
vprefetche1 2112+a(%r11,%rbx,8) #81.6 c37
vfmadd213pd 64+a(%r11,%rbx,8), %zmm0, %zmm3 #81.6 c41
vprefetch0 832+a(%r11,%rbx,8) #81.6 c45
vfmadd213pd 128+a(%r11,%rbx,8), %zmm0, %zmm4 #81.6 c49
vprefetch1 2112+c(%r15,%rbx,8) #81.28 c53
vfmadd213pd 192+a(%r11,%rbx,8), %zmm0, %zmm5 #81.6 c57
vprefetch0 832+c(%r15,%rbx,8) #81.28 c61
vmovaps %zmm1, a(%r11,%rbx,8) #81.6 c65
vprefetche1 2176+a(%r11,%rbx,8) #81.6 c69
vmovaps %zmm3, 64+a(%r11,%rbx,8) #81.6 c73
vprefetch0 896+a(%r11,%rbx,8) #81.6 c77
vmovaps %zmm4, 128+a(%r11,%rbx,8) #81.6 c81
vprefetch1 2176+c(%r15,%rbx,8) #81.28 c85
vmovaps %zmm5, 192+a(%r11,%rbx,8) #81.6 c89
vprefetch0 896+c(%r15,%rbx,8) #81.28 c93
vprefetche1 2240+a(%r11,%rbx,8) #81.6 c97
vprefetch0 960+a(%r11,%rbx,8) #81.6 c101
vprefetch1 2240+c(%r15,%rbx,8) #81.28 c105
vprefetch0 960+c(%r15,%rbx,8) #81.28 c109
addq $32, %rbx #77.4 c113
cmpq %rsi, %rbx #77.4 c117
jb ..B1.51 # Prob 99% #77.4 c117
带有填充的慢速乘法看起来像这样:
vloadunpackld (%rbx), %zmm0 #83.6 c1
addl $32, %r15d #81.10 c1
vprefetch1 2048+c(%rcx) #83.30 c5
vloadunpackhd 64(%rbx), %zmm0 #83.6 c9
addq $256, %rbx #81.10 c9
vprefetch0 512+c(%rcx) #83.30 c13
vbroadcastsd b(%r12,%r13,8), %zmm2 #83.18 c17
vprefetch1 2112+c(%rcx) #83.30 c21
vfmadd132pd c(%rcx), %zmm0, %zmm2 #83.6 c25
vprefetch0 576+c(%rcx) #83.30 c29
vpackstoreld %zmm2, (%rsi) #83.6 c33
vprefetch1 2176+c(%rcx) #83.30 c37
vpackstorehd %zmm2, 64(%rsi) #83.6 c41
addq $256, %rsi #81.10 c41
vprefetch0 640+c(%rcx) #83.30 c45
vloadunpackld (%rdi), %zmm3 #83.6 c49
vprefetch1 2240+c(%rcx) #83.30 c53
vloadunpackhd 64(%rdi), %zmm3 #83.6 c57
addq $256, %rdi #81.10 c57
vprefetch0 704+c(%rcx) #83.30 c61
vbroadcastsd b(%r12,%r13,8), %zmm4 #83.18 c65
vfmadd132pd 64+c(%rcx), %zmm3, %zmm4 #83.6 c69
nop #83.6 c73
vpackstoreld %zmm4, (%r8) #83.6 c77
vpackstorehd %zmm4, 64(%r8) #83.6 c81
addq $256, %r8 #81.10 c81
vloadunpackld (%r9), %zmm5 #83.6 c85
vloadunpackhd 64(%r9), %zmm5 #83.6 c89
addq $256, %r9 #81.10 c89
vbroadcastsd b(%r12,%r13,8), %zmm6 #83.18 c93
vfmadd132pd 128+c(%rcx), %zmm5, %zmm6 #83.6 c97
nop #83.6 c101
vpackstoreld %zmm6, (%r10) #83.6 c105
vpackstorehd %zmm6, 64(%r10) #83.6 c109
addq $256, %r10 #81.10 c109
vloadunpackld (%r11), %zmm7 #83.6 c113
vloadunpackhd 64(%r11), %zmm7 #83.6 c117
addq $256, %r11 #81.10 c117
vbroadcastsd b(%r12,%r13,8), %zmm8 #83.18 c121
vfmadd132pd 192+c(%rcx), %zmm7, %zmm8 #83.6 c125
addq $256, %rcx #81.10 c129
movb %al, %al #83.6 c129
vpackstoreld %zmm8, (%rdx) #83.6 c133
vpackstorehd %zmm8, 64(%rdx) #83.6 c137
addq $256, %rdx #81.10 c137
cmpl $5888, %r15d #81.10 c141
jb ..B1.42 # Prob 99% #81.10 c141
这是我的解决方案的完整代码。同样,如果我将 np 与 n 交换,性能会快两倍以上。
#include <sys/time.h>
#include <omp.h>
#ifndef max
#define max(a,b) (((a) (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif
#define n 5900
#define pn ((((n*sizeof(double))+63)/64)*(64/sizeof(double))) //padding
#define threadNum 144
#define loops 1
double dtime()
{
double tseconds = 0.0;
struct timeval mytime;
gettimeofday(&mytime, (struct timezone*)0);
tseconds = (double)(mytime.tv_sec + mytime.tv_usec*1.0e-6);
return tseconds;
}
double a[n*pn] __attribute__((aligned(64)));
double b[n*pn] __attribute__((aligned(64)));
double c[n*pn] __attribute__((aligned(64)));
main(int argc, char **argv){
int threadNumber, loopNumber;
if(argc == 3)
{
threadNumber = atoi(argv[1]);
loopNumber = atoi(argv[2]);
} else
{
threadNumber = threadNum;
loopNumber = loops;
}
double tstart, tstop, ttime;
int i,j,k,h;
// initialize matrices
#pragma omp parallel for
for(i = 0; i < pn*n; i++)
{
a[i]=0.0;
b[i]=2.0;
c[i]=2.0;
}
omp_set_num_threads(threadNumber);
tstart = dtime();
//parallelize via OpenMP on MIC
for(h = 0; h < loopNumber; h++){
#pragma omp parallel for shared(a,b,c) private(h,i,j,k)
for( i = 0; i < n; i++ )
{
for( k = 0; k < n; k++ )
{
#pragma omp simd aligned( a, b, c: 64 )
for( j = 0; j < n; j++)
{
a[i*pn+j]+= b[i*pn+k] * c[k*pn +j];
}
}
}
}
tstop = dtime();
double elapsed = tstop - tstart;
double mFlops = ((double)n)*n*n*2.0*loopNumber/elapsed*1.0e-06;
#pragma omp parallel
#pragma omp master
printf("%d %.3f\n", omp_get_num_threads(), mFlops);
}
最佳答案
您提供的代码片段太小,无法进行适当的测试我可以在不进行适当测试的情况下提出解决方案,因此它可能会惨败。无论如何,这里什么都没有。
您说您确实填充了数据以对齐行,但是,AFAICS,此信息永远不会传输到编译器,因此无法利用它。为此,您有多种解决方案,但由于您已经拥有用于并行化的 OpenMP 指令,因此使用更多指令进行矢量化似乎是显而易见的选择。
所以这会给你这样的东西:
#pragma omp parallel for private( i, j, k )
for( i = 0; i < n; i++ ) {
double *aa = &a[i * pn];
double *bb = &b[i * pn];
for( k = 0; k < n; k++ ) {
double *cc = &c[k * pn];
#pragma omp simd aligned( aa, bb, cc: 64 )
for( j = 0; j < n; j++) {
aa[j] += bb[k] * cc[j];
}
}
}
这里我假设你的数组是 double
所以你可能需要改变它们不是。除此之外,我认为这个想法是存在的。我不知道它是否适合您,但这绝对值得一试。
关于c - 至强融核 : slower performance with padding,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37663541/
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!