_mm_load1_pd
时间: 2023-09-01 14:07:14 浏览: 27
_mm_load1_pd 是 Intel Intrinsics 库中的一个函数,用于加载一个双精度浮点数(double)到一个 __m128d 类型的向量中的两个元素中的一个,另一个元素被设置为 0。它的声明如下:
```c
__m128d _mm_load1_pd (double const * mem_addr);
```
其中,参数 mem_addr 是一个 double 类型的指针,指向要加载的内存地址。函数返回一个 __m128d 类型的向量,其中的第一个元素和第二个元素都被设置为 mem_addr 指向的 double 值。这个函数可以用来把一个双精度浮点数广播(broadcast)到一个向量中的多个元素中,从而可以用 SIMD 指令同时处理多个数据。
相关问题
编写sse和超线程提高hpl性能的实验代码
以下是一个使用SSE和超线程的HPL性能实验代码:
```c++
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <xmmintrin.h>
#include <omp.h>
#define HPL_LARGEST_N 100000
void dgemm_sse(double *A, double *B, double *C, int n) {
__m128d a_line, b_line, c_line, c_line0, c_line1, c_line2, c_line3;
double *pb, *pc0, *pc1, *pc2, *pc3;
int i, j, k;
for (i = 0; i < n; i += 2) {
for (j = 0; j < n; j += 4) {
c_line0 = _mm_load_pd(C + i * n + j);
c_line1 = _mm_load_pd(C + i * n + j + 2);
c_line2 = _mm_load_pd(C + (i + 1) * n + j);
c_line3 = _mm_load_pd(C + (i + 1) * n + j + 2);
for (k = 0, pb = B + j, pc0 = C + i * n + j, pc1 = C + i * n + j + 2, pc2 = C + (i + 1) * n + j, pc3 = C + (i + 1) * n + j + 2; k < n; ++k, pb += 4, pc0 += 2, pc1 += 2, pc2 += 2, pc3 += 2) {
a_line = _mm_load1_pd(A + i * n + k);
b_line = _mm_load_pd(pb);
c_line = _mm_load_pd(pc0);
c_line = _mm_add_pd(c_line, _mm_mul_pd(a_line, b_line));
_mm_store_pd(pc0, c_line);
b_line = _mm_load_pd(pb + 2);
c_line = _mm_load_pd(pc1);
c_line = _mm_add_pd(c_line, _mm_mul_pd(a_line, b_line));
_mm_store_pd(pc1, c_line);
a_line = _mm_load1_pd(A + (i + 1) * n + k);
b_line = _mm_load_pd(pb);
c_line = _mm_load_pd(pc2);
c_line = _mm_add_pd(c_line, _mm_mul_pd(a_line, b_line));
_mm_store_pd(pc2, c_line);
b_line = _mm_load_pd(pb + 2);
c_line = _mm_load_pd(pc3);
c_line = _mm_add_pd(c_line, _mm_mul_pd(a_line, b_line));
_mm_store_pd(pc3, c_line);
}
_mm_store_pd(C + i * n + j, c_line0);
_mm_store_pd(C + i * n + j + 2, c_line1);
_mm_store_pd(C + (i + 1) * n + j, c_line2);
_mm_store_pd(C + (i + 1) * n + j + 2, c_line3);
}
}
}
void dgemm_sse_omp(double *A, double *B, double *C, int n) {
#pragma omp parallel for
for (int i = 0; i < n; i += 2) {
for (int j = 0; j < n; j += 4) {
__m128d a_line, b_line, c_line, c_line0, c_line1, c_line2, c_line3;
double *pb, *pc0, *pc1, *pc2, *pc3;
c_line0 = _mm_load_pd(C + i * n + j);
c_line1 = _mm_load_pd(C + i * n + j + 2);
c_line2 = _mm_load_pd(C + (i + 1) * n + j);
c_line3 = _mm_load_pd(C + (i + 1) * n + j + 2);
for (int k = 0, pb = B + j, pc0 = C + i * n + j, pc1 = C + i * n + j + 2, pc2 = C + (i + 1) * n + j, pc3 = C + (i + 1) * n + j + 2; k < n; ++k, pb += 4, pc0 += 2, pc1 += 2, pc2 += 2, pc3 += 2) {
a_line = _mm_load1_pd(A + i * n + k);
b_line = _mm_load_pd(pb);
c_line = _mm_load_pd(pc0);
c_line = _mm_add_pd(c_line, _mm_mul_pd(a_line, b_line));
_mm_store_pd(pc0, c_line);
b_line = _mm_load_pd(pb + 2);
c_line = _mm_load_pd(pc1);
c_line = _mm_add_pd(c_line, _mm_mul_pd(a_line, b_line));
_mm_store_pd(pc1, c_line);
a_line = _mm_load1_pd(A + (i + 1) * n + k);
b_line = _mm_load_pd(pb);
c_line = _mm_load_pd(pc2);
c_line = _mm_add_pd(c_line, _mm_mul_pd(a_line, b_line));
_mm_store_pd(pc2, c_line);
b_line = _mm_load_pd(pb + 2);
c_line = _mm_load_pd(pc3);
c_line = _mm_add_pd(c_line, _mm_mul_pd(a_line, b_line));
_mm_store_pd(pc3, c_line);
}
_mm_store_pd(C + i * n + j, c_line0);
_mm_store_pd(C + i * n + j + 2, c_line1);
_mm_store_pd(C + (i + 1) * n + j, c_line2);
_mm_store_pd(C + (i + 1) * n + j + 2, c_line3);
}
}
}
void dgemm(double *A, double *B, double *C, int n) {
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
double cij = C[i * n + j];
for (int k = 0; k < n; ++k) {
cij += A[i * n + k] * B[k * n + j];
}
C[i * n + j] = cij;
}
}
}
int main(int argc, char **argv) {
int n, nb, pm, pn, i, j, size;
double *A, *B, *C, *D, *E, t1, t2, t3, t4, mflops;
n = atoi(argv[1]);
nb = (n > 10000) ? 512 : 128;
pm = (n / nb) * nb;
pn = (n % nb) ? n % nb : nb;
size = pm * pm + 2 * pm * pn + pn * pn;
A = (double *) _mm_malloc(size * sizeof(double), 16);
B = A + pm * pm;
C = B + pm * pn;
D = (double *) _mm_malloc(size * sizeof(double), 16);
E = D + pm * pm;
double *tmp;
for (i = 0; i < n; ++i) {
for (j = 0; j < n; ++j) {
A[i * pm + j] = ((double) rand() / (RAND_MAX)) * 2 - 1;
D[i * pm + j] = ((double) rand() / (RAND_MAX)) * 2 - 1;
}
}
for (i = 0; i < pm; ++i) {
for (j = 0; j < pn; ++j) {
B[i * pn + j] = ((double) rand() / (RAND_MAX)) * 2 - 1;
C[i * pn + j] = 0.0;
E[i * pn + j] = 0.0;
}
}
// warm up
dgemm(A, B, C, n);
dgemm_sse(A, B, E, n);
dgemm_sse_omp(A, B, E, n);
// time regular version
t1 = omp_get_wtime();
dgemm(A, B, C, n);
t2 = omp_get_wtime();
printf("Regular version: %.2f seconds\n", t2 - t1);
// time SSE version
t3 = omp_get_wtime();
dgemm_sse(A, B, E, n);
t4 = omp_get_wtime();
printf("SSE version: %.2f seconds\n", t4 - t3);
// time SSE+OMP version
t3 = omp_get_wtime();
dgemm_sse_omp(A, B, E, n);
t4 = omp_get_wtime();
printf("SSE+OMP version: %.2f seconds\n", t4 - t3);
// verify results
for (i = 0; i < pm; ++i) {
for (j = 0; j < pn; ++j) {
if (C[i * pn + j] - E[i * pn + j] > 1e-6 || C[i * pn + j] - E[i * pn + j] < -1e-6) {
printf("Verification failed: %lf != %lf\n", C[i * pn + j], E[i * pn + j]);
return -1;
}
}
}
// compute MFLOPS
t3 = omp_get_wtime();
for (int iter = 0; iter < 10; ++iter) {
dgemm_sse_omp(A, B, E, n);
}
t4 = omp_get_wtime();
mflops = 1e-6 * 2 * n * n * n / ((t4 - t3) / 10.0);
printf("MFLOPS: %lf\n", mflops);
// clean up
_mm_free(A);
_mm_free(D);
return 0;
}
```
在这个实验代码中,我们使用了SSE指令来加速矩阵乘法操作,并且使用OpenMP来并行化代码以提高性能。我们也将代码进行了优化,使用了缓存优化以减少内存访问开销。
在主函数中,我们首先生成随机矩阵A、B和C,然后分别调用了三种不同的矩阵乘法函数,即普通的矩阵乘法函数,使用SSE指令的矩阵乘法函数和同时使用SSE指令和OpenMP并行化的矩阵乘法函数。我们分别计算了这三种函数的运行时间,并进行了结果验证和性能评测。
需要注意的是,由于SSE指令要求数据对齐,我们使用了`_mm_malloc`函数来分配内存,并将地址对齐到16字节边界。我们也使用了`_mm_free`函数来释放内存。
/* 可以修改的代码区域 */ // ----------------------------------- for (int i = 0; i < MAXN; ++i) { c[i] += a[i] * b[i]; } // ----------------------------------- 用SIMD指令集优化
使用SIMD(Single Instruction, Multiple Data)指令集可以实现对向量操作的并行计算,从而优化代码性能。在这个例子中,我们可以使用SIMD指令集来优化循环计算。
首先,确保你的编译器支持SIMD指令集,并且已经打开了相应的优化选项。不同的编译器有不同的选项,例如GCC可以使用"-march=native"选项来启用最优的指令集。
接下来,我们可以将循环内的操作改为使用SIMD指令集提供的向量化函数。在C/C++中,常见的SIMD指令集有SSE、AVX等,可以使用相应的库来调用这些指令集。
以下是一个示例代码,使用AVX指令集进行向量化计算:
```cpp
#include <immintrin.h>
// ...
for (int i = 0; i < MAXN; i += 8) // 假设MAXN是8的倍数
{
__m256d va = _mm256_load_pd(&a[i]); // 加载8个双精度浮点数到寄存器
__m256d vb = _mm256_load_pd(&b[i]);
__m256d vc = _mm256_load_pd(&c[i]);
__m256d result = _mm256_mul_pd(va, vb); // 向量乘法运算
result = _mm256_add_pd(result, vc); // 向量加法运算
_mm256_store_pd(&c[i], result); // 存储结果回内存
}
```
上述代码中,我们使用了AVX指令集提供的256位寄存器(__m256d)和相应的向量化函数(_mm256_load_pd、_mm256_mul_pd、_mm256_add_pd、_mm256_store_pd)来进行向量化计算。这样可以一次性处理8个双精度浮点数,提高计算效率。
请注意,向量化计算可能需要满足一些特定的条件,例如数组长度是向量长度的整数倍等。同时,不同的编译器和硬件平台可能对SIMD指令集的支持程度有所差异,具体的优化效果需要根据实际情况进行测试和调整。
相关推荐







