Мне нужна некоторая идея, как написать C ++ кросс-платформенное внедрение нескольких parallelizable проблем способом, таким образом, я могу использовать в своих интересах SIMD (SSE, SPU, и т.д.) при наличии. А также я хочу быть в состоянии во время, которым управляют, переключиться между SIMD и не SIMD.
Как Вы предложили бы, чтобы я приблизился к этой проблеме? (Конечно, я не хочу осуществлять проблему многократно для всех возможных вариантов),
Я вижу, как это не могло бы быть очень легкой задачей с C ++, но я полагаю, что пропускаю что-то. До сих пор моя идея похожа на это... Класс cStream будет множеством единственной области. Используя несколько cStreams я могу достигнуть SoA (Структура Множеств). Тогда используя несколько Функторов я могу фальсифицировать функцию Лямбды, что я должен быть казнен по целому cStream.
// just for example I'm not expecting this code to compile
cStream a; // something like float[1024]
cStream b;
cStream c;
void Foo()
{
for_each(
AssignSIMD(c, MulSIMD(AddSIMD(a, b), a)));
}
Где for_each будет ответственен за увеличивание текущего указателя потоков, а также inlining тело функторов с SIMD и без SIMD.
что-то как так:
// just for example I'm not expecting this code to compile
for_each(functor<T> f)
{
#ifdef USE_SIMD
if (simdEnabled)
real_for_each(f<true>()); // true means use SIMD
else
#endif
real_for_each(f<false>());
}
Заметьте, что, если SIMD позволен, проверен однажды и что петля вокруг главного функтора.
Если кто-то заинтересован, это грязный код, который я прихожу с тем, чтобы проверить новый Идея, с которой я пришел, читая о библиотеке, которую Пол опубликовал.
Спасибо Пол!
// This is just a conceptual test
// I haven't profile the code and I haven't verified if the result is correct
#include <xmmintrin.h>
// This class is doing all the math
template <bool SIMD>
class cStreamF32
{
private:
void* m_data;
void* m_dataEnd;
__m128* m_current128;
float* m_current32;
public:
cStreamF32(int size)
{
if (SIMD)
m_data = _mm_malloc(sizeof(float) * size, 16);
else
m_data = new float[size];
}
~cStreamF32()
{
if (SIMD)
_mm_free(m_data);
else
delete[] (float*)m_data;
}
inline void Begin()
{
if (SIMD)
m_current128 = (__m128*)m_data;
else
m_current32 = (float*)m_data;
}
inline bool Next()
{
if (SIMD)
{
m_current128++;
return m_current128 < m_dataEnd;
}
else
{
m_current32++;
return m_current32 < m_dataEnd;
}
}
inline void operator=(const __m128 x)
{
*m_current128 = x;
}
inline void operator=(const float x)
{
*m_current32 = x;
}
inline __m128 operator+(const cStreamF32<true>& x)
{
return _mm_add_ss(*m_current128, *x.m_current128);
}
inline float operator+(const cStreamF32<false>& x)
{
return *m_current32 + *x.m_current32;
}
inline __m128 operator+(const __m128 x)
{
return _mm_add_ss(*m_current128, x);
}
inline float operator+(const float x)
{
return *m_current32 + x;
}
inline __m128 operator*(const cStreamF32<true>& x)
{
return _mm_mul_ss(*m_current128, *x.m_current128);
}
inline float operator*(const cStreamF32<false>& x)
{
return *m_current32 * *x.m_current32;
}
inline __m128 operator*(const __m128 x)
{
return _mm_mul_ss(*m_current128, x);
}
inline float operator*(const float x)
{
return *m_current32 * x;
}
};
// Executes both functors
template<class T1, class T2>
void Execute(T1& functor1, T2& functor2)
{
functor1.Begin();
do
{
functor1.Exec();
}
while (functor1.Next());
functor2.Begin();
do
{
functor2.Exec();
}
while (functor2.Next());
}
// This is the implementation of the problem
template <bool SIMD>
class cTestFunctor
{
private:
cStreamF32<SIMD> a;
cStreamF32<SIMD> b;
cStreamF32<SIMD> c;
public:
cTestFunctor() : a(1024), b(1024), c(1024) { }
inline void Exec()
{
c = a + b * a;
}
inline void Begin()
{
a.Begin();
b.Begin();
c.Begin();
}
inline bool Next()
{
a.Next();
b.Next();
return c.Next();
}
};
int main (int argc, char * const argv[])
{
cTestFunctor<true> functor1;
cTestFunctor<false> functor2;
Execute(functor1, functor2);
return 0;
}
Вы можете посмотреть на источник для библиотеки MACSTL для некоторых идей в этой области: www.pixelglow.com/macstl/
Наиболее впечатляющим подходом к SIMD-масштабированию, которые я видел, является Рамочная структура RRTFACT RAY: Slides , , . Нужно стоит взглянуть. Исследователи тесно связаны с Intel (Saarbrucken теперь проходит институт Intel Visual Computing Computing), чтобы вы могли быть уверены, что масштабирование на AVX и Larrabee было на уме.
Intel CT «Параллельность данных» библиотека шаблона «Параллельность данных» выглядит довольно многообещающе.
No. Необходимо выполнить fflush (stdout); Многие реализации будут очищаться в каждой новой строке, когда они отправляют выходные данные на терминал.
-121--2402906- Чтобы ответить на ваш вопрос, вам нужно дополнительное fflush (stdout);
после вызова printf ()
, чтобы убедиться, что приглашение появляется перед тем, как программа попытается прочитать входные данные. Чтение из stdin
не fflush (stdout);
для вас.
Вы думали об использовании существующих решений, таких как libuil ? Он реализует множество общих операций SIMD и может во время выполнения решать, использовать ли код SIMD/не SIMD (используя указатели функций, назначенные функцией инициализации).
Обратите внимание, что данный пример решает, что выполнять во время компиляции (так как вы используете препроцессор), в этом случае вы можете использовать более сложные техники, чтобы решить, что вы на самом деле хотите выполнить; например, Tag Dispatch: http://cplusplus.co.il/2010/01/03/tag-dispatching/ Следуя приведенному там примеру, вы можете иметь быструю реализацию с SIMD, а медленную - без.
Вы, возможно, захотите взглянуть на мою попытку создания SIMD/non-SIMD:
vrep, шаблонного базового класса со специализацией на SIMD (обратите внимание на то, как он различает SSE только с плавающей точкой и SSE2, который ввел целочисленные векторы).
Более полезные v4f, v4i и т.д. классы (подкласс через промежуточный v4).
Конечно, для вычислений типа rgba/xyz он гораздо больше ориентирован на 4-элементные векторы, чем на SoA, так что при появлении 8-полосного AVX пар полностью кончится, но общие принципы могут быть полезны.