шаблоны — оператор шаблонного массива C ++ [] с использованием целых

Я пытаюсь манипулировать специальной структурой, и мне нужен какой-то оператор swizzle. Для этого имеет смысл иметь перегруженный массив [] оператор, но я не хочу иметь никакого ветвления, так как конкретная спецификация структуры допускает теоретический обходной путь.

В настоящее время структура выглядит так:

struct f32x4
{
float fLow[2];
float fHigh[2];

f32x4(float a, float b, float c, float d)
{
fLow[0] = a;
fLow[1] = b;
fHigh[0] = c;
fHigh[1] = d;
}

// template with an int here?
inline float& operator[] (int x) {
if (x < 2)
return fLow[x];
else
return fHigh[x - 2];
}
};

Что я мог / должен сделать, чтобы избежать ветки? Моя идея состоит в том, чтобы использовать шаблон с целочисленным параметром и определять специализации, но не ясно, имеет ли он смысл и как может выглядеть синтаксис этого монстра.

Я явно, ни при каких обстоятельствах, не могу использовать float[4] массив для объединения двух (также, без трюков объединения). Если вам нужна веская причина для этого, это потому, что float[2] на самом деле напоминают платформу PowerPC парных синглов. Обычный компилятор Windows не будет работать с парными синглами, поэтому я заменил код на float[2]s.

Используя компилятор GreenHills, я получаю вывод этой сборки (который предполагает, что ветвление действительно происходит):

.LDW31:
00000050 80040000           89      lwz r0, 0(r4)
00000054 2c000000           90      cmpwi   r0, 0
00000058 41820000           91      beq .L69
92  #line32
93
94  .LDWlin1:
0000005c 2c000001           95      cmpwi   r0, 1
00000060 40820000           96      bne .L74
97  #line32
98
99  .LDWlin2:
00000064 38630004          100      addi    r3, r3, 4
00000068 38210018          101      addi    sp, sp, 24
0000006c 4e800020          102      blr
103  .L74:
00000070 2c000002          104      cmpwi   r0, 2
00000074 40820000          105      bne .L77
106  #line33
107
108  .LDWlin3:
00000078 38630008          109      addi    r3, r3, 8
0000007c 38210018          110      addi    sp, sp, 24
00000080 4e800020          111      blr
112  .L77:
00000084 2c000003          113      cmpwi   r0, 3
00000088 40820000          114      bne .L80
115  #line34
116
117  .LDWlin4:
0000008c 3863000c          118      addi    r3, r3, 12
00000090 38210018          119      addi    sp, sp, 24
00000094 4e800020          120      blr
121  .L80:
00000098 38610008          122      addi    r3, sp, 8
123  .L69:
124  #       .ef

Соответствующий код C ++ этому фрагменту должен быть таким:

 inline const float& operator[](const unsigned& idx) const
{
if (idx == 0)  return xy[0];
if (idx == 1)  return xy[1];
if (idx == 2)  return zw[0];
if (idx == 3)  return zw[1];
return 0.f;
}

3

Решение

Поскольку вы сказали в комментарии, что ваш индекс всегда является параметром шаблона, то вы действительно можете выполнить ветвление во время компиляции, а не во время выполнения. Вот возможное решение с использованием std::enable_if:

#include <iostream>
#include <type_traits>

struct f32x4
{
float fLow[2];
float fHigh[2];

f32x4(float a, float b, float c, float d)
{
fLow[0] = a;
fLow[1] = b;
fHigh[0] = c;
fHigh[1] = d;
}

template <int x>
float& get(typename std::enable_if<(x >= 0 && x < 2)>::type* = 0)
{
return fLow[x];
}

template <int x>
float& get(typename std::enable_if<(x >= 2 && x < 4)>::type* = 0)
{
return fHigh[x-2];
}
};

int main()
{
f32x4 f(0.f, 1.f, 2.f, 3.f);

std::cout << f.get<0>() << " " << f.get<1>() << " "<< f.get<2>() << " " << f.get<3>(); // prints 0 1 2 3
}

Что касается производительности, я не думаю, что будет какая-либо разница, так как оптимизатор должен иметь возможность легко распространять константы и впоследствии удалять мертвый код, тем самым полностью удаляя ветвь. Однако при таком подходе вы получаете преимущество, заключающееся в том, что любые попытки вызвать функцию с недопустимым индексом приведут к ошибке компилятора.

3

Другие решения

Либо индекс x переменная времени выполнения или константа времени компиляции.

  • если это константа времени компиляции, есть большая вероятность, что оптимизатор сможет удалить мертвую ветвь при вставке operator[] тем не мение.

  • если это переменная времени выполнения, например

    for (int i=0; i<4; ++i) { dosomething(f[i]); }
    

    вам все равно нужна ветка. Если, конечно, ваш оптимизатор не развернет цикл, в этом случае он может заменить переменную четырьмя константами, встроенными & обрезать, как указано выше.

Вы профилировали это, чтобы показать, что есть реальная проблема, и скомпилировали это, чтобы показать, что ветка действительно происходит там, где ее можно избежать?


Пример кода:

float foo(f32x4 &f)
{
return f[0]+f[1]+f[2]+f[3];
}

пример вывода из g++ -O3 -S

.globl _Z3fooR5f32x4
.type       _Z3fooR5f32x4, @function
_Z3fooR5f32x4:
.LFB4:
.cfi_startproc
movss       (%rdi), %xmm0
addss       4(%rdi), %xmm0
addss       8(%rdi), %xmm0
addss       12(%rdi), %xmm0
ret
.cfi_endproc
6

Серьезно, не делай этого !! Просто объедините массивы. Но так как вы задали вопрос, вот ответ:

#include <iostream>

float fLow [2] = {1.0,2.0};
float fHigh [2] = {50.0,51.0};

float * fArrays[2] = {fLow, fHigh};

float getFloat (int i)
{
return fArrays[i>=2][i%2];
}

int main()
{
for (int i = 0; i < 4; ++i)
std::cout << getFloat(i) << '\n';
return 0;
}

Выход:

1
2
50
51
4

Создайте один массив (или вектор) со всеми 4 элементами в нем, значения fLow занимают первые две позиции, а затем высокие во второй 2. Затем просто внесите в него индекс.

inline float& operator[] (int x) {
return newFancyArray[x]; //But do some bounds checking above.
}
1

Основываясь на ответе Люка Турайля, не используя черты типа из-за отсутствия поддержки компилятора, я нашел следующее для достижения цели вопроса. Так как operator [] не может быть шаблонизирован с параметром int и работает синтаксически, я ввел at метод. Это результат:

struct f32x4
{
float fLow[2];
float fHigh[2];

f32x4(float a, float b, float c, float d)
{
fLow[0] = a;
fLow[1] = b;
fHigh[0] = c;
fHigh[1] = d;
}template <unsigned T>
const float& at() const;

};
template<>
const float& f32x4::at<0>() const { return fLow[0]; }
template<>
const float& f32x4::at<1>() const { return fLow[1]; }
template<>
const float& f32x4::at<2>() const { return fHigh[0]; }
template<>
const float& f32x4::at<3>() const { return fHigh[1]; }
0
По вопросам рекламы [email protected]