// Copyright 2021 DeepMind Technologies Limited // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "engine/engine_util_blas.h" #include #include #ifdef mjUSEPLATFORMSIMD #if defined(__AVX__) && defined(mjUSEDOUBLE) #define mjUSEAVX #include "immintrin.h" #endif #endif //------------------------------ 3D vector and matrix-vector operations ---------------------------- // res = 0 void mju_zero3(mjtNum res[3]) { res[0] = 0; res[1] = 0; res[2] = 0; } // res = vec void mju_copy3(mjtNum res[3], const mjtNum data[3]) { res[0] = data[0]; res[1] = data[1]; res[2] = data[2]; } // res = vec*scl void mju_scl3(mjtNum res[3], const mjtNum vec[3], mjtNum scl) { res[0] = vec[0] * scl; res[1] = vec[1] * scl; res[2] = vec[2] * scl; } // res = vec1 + vec2 void mju_add3(mjtNum res[3], const mjtNum vec1[3], const mjtNum vec2[3]) { res[0] = vec1[0] + vec2[0]; res[1] = vec1[1] + vec2[1]; res[2] = vec1[2] + vec2[2]; } // res = vec1 - vec2 void mju_sub3(mjtNum res[3], const mjtNum vec1[3], const mjtNum vec2[3]) { res[0] = vec1[0] - vec2[0]; res[1] = vec1[1] - vec2[1]; res[2] = vec1[2] - vec2[2]; } // res += vec void mju_addTo3(mjtNum res[3], const mjtNum vec[3]) { res[0] += vec[0]; res[1] += vec[1]; res[2] += vec[2]; } // res -= vec void mju_subFrom3(mjtNum res[3], const mjtNum vec[3]) { res[0] -= vec[0]; res[1] -= vec[1]; res[2] -= vec[2]; } // res += vec*scl void mju_addToScl3(mjtNum res[3], const mjtNum vec[3], mjtNum scl) { res[0] += vec[0] * scl; res[1] += vec[1] * scl; res[2] += vec[2] * scl; } // res = vec1 + vec2*scl void mju_addScl3(mjtNum res[3], const mjtNum vec1[3], const mjtNum vec2[3], mjtNum scl) { res[0] = vec1[0] + scl*vec2[0]; res[1] = vec1[1] + scl*vec2[1]; res[2] = vec1[2] + scl*vec2[2]; } // normalize vector, return length before normalization mjtNum mju_normalize3(mjtNum vec[3]) { mjtNum norm = mju_sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]); if (norm0) { memset(res, 0, n*sizeof(mjtNum)); } } // res = vec void mju_copy(mjtNum* res, const mjtNum* vec, int n) { if (n>0) { memcpy(res, vec, n*sizeof(mjtNum)); } } // sum(vec) mjtNum mju_sum(const mjtNum* vec, int n) { mjtNum res = 0; for (int i=0; i=0) { __m256d sclpar, val1, val1scl; // init sclpar = _mm256_set1_pd(scl); // parallel computation while (i<=n_4) { val1 = _mm256_loadu_pd(vec+i); val1scl = _mm256_mul_pd(val1, sclpar); _mm256_storeu_pd(res+i, val1scl); i += 4; } } // process remaining int n_i = n - i; if (n_i==3) { res[i] = vec[i]*scl; res[i+1] = vec[i+1]*scl; res[i+2] = vec[i+2]*scl; } else if (n_i==2) { res[i] = vec[i]*scl; res[i+1] = vec[i+1]*scl; } else if (n_i==1) { res[i] = vec[i]*scl; } #else for (; i=0) { __m256d sum, val1, val2; // parallel computation while (i<=n_4) { val1 = _mm256_loadu_pd(vec1+i); val2 = _mm256_loadu_pd(vec2+i); sum = _mm256_add_pd(val1, val2); _mm256_storeu_pd(res+i, sum); i += 4; } } // process remaining int n_i = n - i; if (n_i==3) { res[i] = vec1[i] + vec2[i]; res[i+1] = vec1[i+1] + vec2[i+1]; res[i+2] = vec1[i+2] + vec2[i+2]; } else if (n_i==2) { res[i] = vec1[i] + vec2[i]; res[i+1] = vec1[i+1] + vec2[i+1]; } else if (n_i==1) { res[i] = vec1[i] + vec2[i]; } #else for (; i=0) { __m256d dif, val1, val2; // parallel computation while (i<=n_4) { val1 = _mm256_loadu_pd(vec1+i); val2 = _mm256_loadu_pd(vec2+i); dif = _mm256_sub_pd(val1, val2); _mm256_storeu_pd(res+i, dif); i += 4; } } // process remaining int n_i = n - i; if (n_i==3) { res[i] = vec1[i] - vec2[i]; res[i+1] = vec1[i+1] - vec2[i+1]; res[i+2] = vec1[i+2] - vec2[i+2]; } else if (n_i==2) { res[i] = vec1[i] - vec2[i]; res[i+1] = vec1[i+1] - vec2[i+1]; } else if (n_i==1) { res[i] = vec1[i] - vec2[i]; } #else for (; i=0) { __m256d sum, val1, val2; // parallel computation while (i<=n_4) { val1 = _mm256_loadu_pd(res+i); val2 = _mm256_loadu_pd(vec+i); sum = _mm256_add_pd(val1, val2); _mm256_storeu_pd(res+i, sum); i += 4; } } // process remaining int n_i = n - i; if (n_i==3) { res[i] += vec[i]; res[i+1] += vec[i+1]; res[i+2] += vec[i+2]; } else if (n_i==2) { res[i] += vec[i]; res[i+1] += vec[i+1]; } else if (n_i==1) { res[i] += vec[i]; } #else for (; i=0) { __m256d dif, val1, val2; // parallel computation while (i<=n_4) { val1 = _mm256_loadu_pd(res+i); val2 = _mm256_loadu_pd(vec+i); dif = _mm256_sub_pd(val1, val2); _mm256_storeu_pd(res+i, dif); i += 4; } } // process remaining int n_i = n - i; if (n_i==3) { res[i] -= vec[i]; res[i+1] -= vec[i+1]; res[i+2] -= vec[i+2]; } else if (n_i==2) { res[i] -= vec[i]; res[i+1] -= vec[i+1]; } else if (n_i==1) { res[i] -= vec[i]; } #else for (; i=0) { __m256d sclpar, sum, val1, val2, val2scl; // init sclpar = _mm256_set1_pd(scl); // parallel computation while (i<=n_4) { val1 = _mm256_loadu_pd(res+i); val2 = _mm256_loadu_pd(vec+i); val2scl = _mm256_mul_pd(val2, sclpar); sum = _mm256_add_pd(val1, val2scl); _mm256_storeu_pd(res+i, sum); i += 4; } } // process remaining int n_i = n - i; if (n_i==3) { res[i] += vec[i]*scl; res[i+1] += vec[i+1]*scl; res[i+2] += vec[i+2]*scl; } else if (n_i==2) { res[i] += vec[i]*scl; res[i+1] += vec[i+1]*scl; } else if (n_i==1) { res[i] += vec[i]*scl; } #else for (; i=0) { __m256d sclpar, sum, val1, val2, val2scl; // init sclpar = _mm256_set1_pd(scl); // parallel computation while (i<=n_4) { val1 = _mm256_loadu_pd(vec1+i); val2 = _mm256_loadu_pd(vec2+i); val2scl = _mm256_mul_pd(val2, sclpar); sum = _mm256_add_pd(val1, val2scl); _mm256_storeu_pd(res+i, sum); i += 4; } } // process remaining int n_i = n - i; if (n_i==3) { res[i] = vec1[i] + vec2[i]*scl; res[i+1] = vec1[i+1] + vec2[i+1]*scl; res[i+2] = vec1[i+2] + vec2[i+2]*scl; } else if (n_i==2) { res[i] = vec1[i] + vec2[i]*scl; res[i+1] = vec1[i+1] + vec2[i+1]*scl; } else if (n_i==1) { res[i] = vec1[i] + vec2[i]*scl; } #else for (; i=0) { __m256d sum, prod, val1, val2; __m128d vlow, vhigh, high64; // init val1 = _mm256_loadu_pd(vec1); val2 = _mm256_loadu_pd(vec2); sum = _mm256_mul_pd(val1, val2); i = 4; // parallel computation while (i<=n_4) { val1 = _mm256_loadu_pd(vec1+i); val2 = _mm256_loadu_pd(vec2+i); prod = _mm256_mul_pd(val1, val2); sum = _mm256_add_pd(sum, prod); i += 4; } // reduce vlow = _mm256_castpd256_pd128(sum); vhigh = _mm256_extractf128_pd(sum, 1); vlow = _mm_add_pd(vlow, vhigh); high64 = _mm_unpackhi_pd(vlow, vlow); res = _mm_cvtsd_f64(_mm_add_sd(vlow, high64)); } #else // do the same order of additions as the AVX intrinsics implementation. // this is faster than the simple for loop you'd expect for a dot product, // and produces exactly the same results. mjtNum res0 = 0; mjtNum res1 = 0; mjtNum res2 = 0; mjtNum res3 = 0; for (; i<=n_4; i+=4) { res0 += vec1[i] * vec2[i]; res1 += vec1[i+1] * vec2[i+1]; res2 += vec1[i+2] * vec2[i+2]; res3 += vec1[i+3] * vec2[i+3]; } res = (res0 + res2) + (res1 + res3); #endif // process remaining int n_i = n - i; if (n_i==3) { res += vec1[i]*vec2[i] + vec1[i+1]*vec2[i+1] + vec1[i+2]*vec2[i+2]; } else if (n_i==2) { res += vec1[i]*vec2[i] + vec1[i+1]*vec2[i+1]; } else if (n_i==1) { res += vec1[i]*vec2[i]; } return res; } //------------------------------ matrix-vector operations ------------------------------------------ // multiply matrix and vector void mju_mulMatVec(mjtNum* res, const mjtNum* mat, const mjtNum* vec, int nr, int nc) { for (int r=0; r