简体   繁体   English

优化2D旋转

[英]Optimising 2D rotation

Given the classic formula for rotating a point in 2D space: 给出在2D空间中旋转点的经典公式:

cv::Point pt[NPOINTS];
cv::Point rotated[NPOINTS];
float angle = WHATEVER;
float cosine = cos(angle);
float sine = sin(angle);

for (int i = 0; i < NPOINTS; i++)
{
    rotated[i].x = pt[i].x * cosine - pt[i].y * sine;
    rotated[i].y = pt[i].x * sine   + pt[i].y * cosine;
}

Given NPOINTS is 32 and the arrays are aligned, how would one go about optimising the code for SSE or AVX? 鉴于NPOINTS是32并且阵列是对齐的,如何优化SSE或AVX的代码? Searching around here and elsewhere didn't turn up anything useful, and I got lost about here: 在这里和其他地方搜索没有发现任何有用的东西,我在这里迷路了:

__m128i onePoint = _mm_set_epi32(pt[i].x, pt[i].y, pt[i].x, pt[i].y);
__m128 onefPoint = _m128_cvtepi32_ps(onePoint);
__m128 sinCos = _mm_set_ps(cosine, -sine, sine, cosine);
__m128 rotated = _mm_mul_ps(onefPoint, sinCos);

But how to go from [y*cosine, -x*sine, x*sine, y*cosine] to [y*cosine + -x*sine, x*sine + y*cosine] ? 但是如何从[y*cosine, -x*sine, x*sine, y*cosine][y*cosine + -x*sine, x*sine + y*cosine] Is this the best approach? 这是最好的方法吗? Does it easily scale to __m512 ? 它容易扩展到__m512吗?

UPDATE : I did a little more research and I now have approximately: 更新 :我做了一些研究,现在我有大约:

__m128i onePoint = _mm_set_epi32(pt[i].x, pt[i].y, pt[i].x, pt[i].y);
__m128 onefPoint = _m128_cvtepi32_ps(onePoint);
__m128i twoPoint = _mm_set_epi32(pt[i+1].x, pt[i+1].y, pt[i+1].x, pt[i+1].y);
__m128 twofPoint = _m128_cvtepi32_ps(twoPoint);
__m128 sinCos = _mm_set_ps(cosine, -sine, sine, cosine);
__m128 rotated1 = _mm_mul_ps(onefPoint, sinCos);
__m128 rotated2 = _mm_mul_ps(twofPoint, sinCos);
__m128 added = _mm_hadd_ps(rotated1, rotated2);
__m128i intResult = _mm_cvtps_epi32(added);
int results[4];
_mm_storeu_si128((__m128i*)results, intResult);

This gives a 50% speed-up from 11% of the processor time to about 6%. 这使得从处理器时间的11%到约6%的速度提高了50%。 Expanding to __m256 and doing four points at a time gives another speed-up. 扩展到__m256并一次做四个点可以提高速度。 This looks quite awful code, but am I heading the right direction? 这看起来非常糟糕,但我正朝着正确的方向前进吗?

Use an array of struct of arrays (AoSoA) and process eight points at a time. 使用数组结构数组(AoSoA)并一次处理八个点。 In the code below point8 is struct of arrays containing eight points. 在下面的代码中, point8是包含八个点的数组结构。 The function rotate_point8 rotates eight points and has the same algebraic structure as the the function rotate_point which rotates a single point. 函数rotate_point8旋转八个点并具有与旋转单个点的函数rotate_point相同的代数结构。 The function rotate_all8 rotates 32 points using the AoSoA point8* . 函数rotate_all8使用AoSoA point8*旋转32个点。

The single point rotation code does 4 multiplications, one addition, and one subtraction. 单点旋转代码进行4次乘法,一次加法和一次减法。

If we look at the assembly for rotate_point8 we see that GCC unrolled the loop and does 4 SIMD multiplications, one SIMD addition, and one SIMD subtraction per unroll. 如果我们查看rotate_point8的程序集,我们会看到GCC展开循环并进行4次SIMD乘法,一次SIMD加法,每次展开一次SIMD减法。 That's the best you can do: eight for the price of one. 这是你能做的最好的事情:一个价格八个。

#include <x86intrin.h>
#include <stdio.h>
#include <math.h>

struct point8 {
  __m256 x;
  __m256 y;
};

struct point {
  float x;
  float y;
};

static point rotate_point(point p, float a, float b) {
  point r;
  r.x = p.x*a - p.y*b;
  r.y = p.x*b + p.y*a;
  return r;
}

static point8 rotate_point8(point8 p, float a, float b) {
  __m256 va = _mm256_set1_ps(a), vb = _mm256_set1_ps(b);
  point8 r;
  r.x = _mm256_sub_ps(_mm256_mul_ps(p.x,va), _mm256_mul_ps(p.y,vb));
  r.y = _mm256_add_ps(_mm256_mul_ps(p.x,vb), _mm256_mul_ps(p.y,va));
  return r;
}

void rotate_all(point* points, point* r, float angle) {
  float a = cos(angle), b = sin(angle);
  for(int i=0; i<32; i++) r[i] = rotate_point(points[i], a, b);
}

void rotate_all8(point8* points, point8* r8, float angle) {
  float a = cos(angle), b = sin(angle);
  for(int i=0; i<4; i++) r8[i] = rotate_point8(points[i], a, b);
}

int main(void) {
  float x[32], y[32];
  point p[32], r[32];
  point8 p8[4], r8[4];
  float angle = 3.14159f/4;

  for(int i=0; i<32; i++) y[i] = 1.0*i/31, x[i] = sqrt(1-y[i]*y[i]);
  for(int i=0; i<32; i++) p[i].x = x[i], p[i].y = y[i];
  for(int i=0; i<4; i++) p8[i].x = _mm256_load_ps(&x[8*i]), p8[i].y = _mm256_load_ps(&y[8*i]); 

  for(int i=0; i<32; i++) printf("%f %f\n", p[i].x, p[i].y); puts("");

  rotate_all(p, r, angle);
  for(int i=0; i<32; i++) printf("%f %f\n", r[i].x, r[i].y); puts("");

  rotate_all8(p8, r8, angle);
  for(int i=0; i<4; i++) {
    _mm256_storeu_ps(x, r8[i].x), _mm256_storeu_ps(y, r8[i].y);
    for(int j=0; j<8; j++) printf("%f %f\n", x[j], y[j]);
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM