Tôi cần một thuật toán chuyển vị trí bộ nhớ nhanh cho hàm convussian Gaussian của tôi trong C/C++. Những gì tôi làm bây giờ làBộ nhớ nhanh chuyển vị trí với SSE, AVX và OpenMP
convolute_1D
transpose
convolute_1D
transpose
Nó chỉ ra rằng với phương pháp này kích thước bộ lọc phải là lớn (hoặc lớn hơn tôi mong đợi) hoặc chuyển vị mất nhiều thời gian hơn so với chập (ví dụ cho một ma trận 1920x1080 chập mất đồng thời với transpose cho kích thước bộ lọc là 35). Thuật toán chuyển đổi hiện tại tôi đang sử dụng sử dụng vòng lặp chặn/ốp lát cùng với SSE và OpenMP. Tôi đã thử một phiên bản sử dụng AVX nhưng nó không nhanh hơn. Bất kỳ đề xuất về cách tôi có thể tăng tốc độ này lên?
inline void transpose4x4_SSE(float *A, float *B, const int lda, const int ldb) {
__m128 row1 = _mm_load_ps(&A[0*lda]);
__m128 row2 = _mm_load_ps(&A[1*lda]);
__m128 row3 = _mm_load_ps(&A[2*lda]);
__m128 row4 = _mm_load_ps(&A[3*lda]);
_MM_TRANSPOSE4_PS(row1, row2, row3, row4);
_mm_store_ps(&B[0*ldb], row1);
_mm_store_ps(&B[1*ldb], row2);
_mm_store_ps(&B[2*ldb], row3);
_mm_store_ps(&B[3*ldb], row4);
}
//block_size = 16 works best
inline void transpose_block_SSE4x4(float *A, float *B, const int n, const int m, const int lda, const int ldb ,const int block_size) {
#pragma omp parallel for
for(int i=0; i<n; i+=block_size) {
for(int j=0; j<m; j+=block_size) {
int max_i2 = i+block_size < n ? i + block_size : n;
int max_j2 = j+block_size < m ? j + block_size : m;
for(int i2=i; i2<max_i2; i2+=4) {
for(int j2=j; j2<max_j2; j2+=4) {
transpose4x4_SSE(&A[i2*lda +j2], &B[j2*ldb + i2], lda, ldb);
}
}
}
}
}
ma trận float Transpose 8x8 sử dụng AVX. Nó không nhanh hơn bốn lần chuyển tiếp 4x4.
inline void transpose8_ps(__m256 &row0, __m256 &row1, __m256 &row2, __m256 &row3, __m256 &row4, __m256 &row5, __m256 &row6, __m256 &row7) {
__m256 __t0, __t1, __t2, __t3, __t4, __t5, __t6, __t7;
__m256 __tt0, __tt1, __tt2, __tt3, __tt4, __tt5, __tt6, __tt7;
__t0 = _mm256_unpacklo_ps(row0, row1);
__t1 = _mm256_unpackhi_ps(row0, row1);
__t2 = _mm256_unpacklo_ps(row2, row3);
__t3 = _mm256_unpackhi_ps(row2, row3);
__t4 = _mm256_unpacklo_ps(row4, row5);
__t5 = _mm256_unpackhi_ps(row4, row5);
__t6 = _mm256_unpacklo_ps(row6, row7);
__t7 = _mm256_unpackhi_ps(row6, row7);
__tt0 = _mm256_shuffle_ps(__t0,__t2,_MM_SHUFFLE(1,0,1,0));
__tt1 = _mm256_shuffle_ps(__t0,__t2,_MM_SHUFFLE(3,2,3,2));
__tt2 = _mm256_shuffle_ps(__t1,__t3,_MM_SHUFFLE(1,0,1,0));
__tt3 = _mm256_shuffle_ps(__t1,__t3,_MM_SHUFFLE(3,2,3,2));
__tt4 = _mm256_shuffle_ps(__t4,__t6,_MM_SHUFFLE(1,0,1,0));
__tt5 = _mm256_shuffle_ps(__t4,__t6,_MM_SHUFFLE(3,2,3,2));
__tt6 = _mm256_shuffle_ps(__t5,__t7,_MM_SHUFFLE(1,0,1,0));
__tt7 = _mm256_shuffle_ps(__t5,__t7,_MM_SHUFFLE(3,2,3,2));
row0 = _mm256_permute2f128_ps(__tt0, __tt4, 0x20);
row1 = _mm256_permute2f128_ps(__tt1, __tt5, 0x20);
row2 = _mm256_permute2f128_ps(__tt2, __tt6, 0x20);
row3 = _mm256_permute2f128_ps(__tt3, __tt7, 0x20);
row4 = _mm256_permute2f128_ps(__tt0, __tt4, 0x31);
row5 = _mm256_permute2f128_ps(__tt1, __tt5, 0x31);
row6 = _mm256_permute2f128_ps(__tt2, __tt6, 0x31);
row7 = _mm256_permute2f128_ps(__tt3, __tt7, 0x31);
}
inline void transpose8x8_avx(float *A, float *B, const int lda, const int ldb) {
__m256 row0 = _mm256_load_ps(&A[0*lda]);
__m256 row1 = _mm256_load_ps(&A[1*lda]);
__m256 row2 = _mm256_load_ps(&A[2*lda]);
__m256 row3 = _mm256_load_ps(&A[3*lda]);
__m256 row4 = _mm256_load_ps(&A[4*lda]);
__m256 row5 = _mm256_load_ps(&A[5*lda]);
__m256 row6 = _mm256_load_ps(&A[6*lda]);
__m256 row7 = _mm256_load_ps(&A[7*lda]);
transpose8_ps(row0, row1, row2, row3, row4, row5, row6, row7);
_mm256_store_ps(&B[0*ldb], row0);
_mm256_store_ps(&B[1*ldb], row1);
_mm256_store_ps(&B[2*ldb], row2);
_mm256_store_ps(&B[3*ldb], row3);
_mm256_store_ps(&B[4*ldb], row4);
_mm256_store_ps(&B[5*ldb], row5);
_mm256_store_ps(&B[6*ldb], row6);
_mm256_store_ps(&B[7*ldb], row7);
}
Điểm tốt. Lý do tôi làm transpose ở nơi đầu tiên là bởi vì đọc dữ liệu không phải là tiếp giáp gây ra rất nhiều lần truy cập bộ nhớ cache. Vì vậy, đó là một cuộc chiến giữa các lần truy cập bộ nhớ cache và thời gian để thực hiện chuyển đổi. Tôi không chắc chắn bằng văn bản ra các kết quả của transpose sẽ giúp đỡ trong convolution mặc dù. Có lẽ tôi phải đưa ra một thuật toán khác nhau cho các kích thước bộ lọc nhỏ hơn. –
Tôi sẽ thực hiện một số kiểm tra về kích thước ma trận nhỏ hơn phù hợp với bộ nhớ cache L2 hoặc L3 và lấy lại cho bạn. Bạn có thể đúng về lý do AVX không xuất hiện tốt hơn trong ví dụ này. –
Tôi đã thử chuyển đổi trên 64x64, 192x192, 896x896 và 5008x5008. Chúng phải tương ứng với các vùng nhớ l1, l2, l3 và bộ nhớ chính của chúng. AVX chỉ nhỉnh hơn SSE 64x64 (bộ nhớ cache L1). Tôi đã tắt OpenMP cho các bài kiểm tra. –