Nano Hash - криптовалюты, майнинг, программирование

Самый эффективный способ хранить 4-точечные произведения в непрерывном массиве на C с использованием встроенных функций SSE.

Я оптимизирую некоторый код для микроархитектуры Intel x86 Nehalem, используя встроенные функции SSE.

Часть моей программы вычисляет 4 скалярных произведения и добавляет каждый результат к предыдущим значениям в непрерывной части массива. В частности,

tmp0 = _mm_dp_ps(A_0m, B_0m, 0xF1);
tmp1 = _mm_dp_ps(A_1m, B_0m, 0xF2);
tmp2 = _mm_dp_ps(A_2m, B_0m, 0xF4);
tmp3 = _mm_dp_ps(A_3m, B_0m, 0xF8);

tmp0 = _mm_add_ps(tmp0, tmp1);
tmp0 = _mm_add_ps(tmp0, tmp2);
tmp0 = _mm_add_ps(tmp0, tmp3);
tmp0 = _mm_add_ps(tmp0, C_0n);

_mm_storeu_ps(C_2, tmp0);

Обратите внимание, что я собираюсь использовать 4 временных регистра xmm для хранения результата каждого скалярного произведения. В каждом регистре xmm результат помещается в уникальные 32 бита относительно других временных регистров xmm, так что конечный результат выглядит следующим образом:

tmp0= R0-ноль-ноль-ноль

tmp1=ноль-R1-ноль-ноль

tmp2=ноль-ноль-R2-ноль

tmp3=ноль-ноль-ноль-R3

Я объединяю значения, содержащиеся в каждой переменной tmp, в одну переменную xmm, суммируя их со следующими инструкциями:

tmp0 = _mm_add_ps(tmp0, tmp1);
tmp0 = _mm_add_ps(tmp0, tmp2);
tmp0 = _mm_add_ps(tmp0, tmp3);

Наконец, я добавляю регистр, содержащий все 4 результата скалярных произведений, в непрерывную часть массива, чтобы индексы массива увеличивались на скалярное произведение, например так (C_0n — это 4 значения, которые в настоящее время находятся в массиве, который необходимо обновить ; C_2 — это адрес, указывающий на эти 4 значения):

tmp0 = _mm_add_ps(tmp0, C_0n);
_mm_storeu_ps(C_2, tmp0);

Я хочу знать, есть ли менее окольный, более эффективный способ взять результаты точечных произведений и добавить их к непрерывному фрагменту массива. Таким образом, я делаю 3 сложения между регистрами, в которых есть только одно ненулевое значение. Кажется, должен быть более эффективный способ сделать это.

Я ценю любую помощь. Спасибо.


Ответы:


1

Для такого кода мне нравится хранить «транспонирование» A и B, так что {A_0m.x, A_1m.x, A_2m.x, A_3m.x} хранятся в одном векторе и т. д. Затем вы можете сделать точечный продукт, использующий только умножение и сложение, и когда вы закончите, у вас есть все 4 точечных продукта в одном векторе без какой-либо перетасовки.

Это часто используется при трассировке лучей для одновременного тестирования 4 лучей относительно плоскости (например, при обходе kd-дерева). Однако, если у вас нет контроля над входными данными, накладные расходы на выполнение транспонирования могут не стоить того. Код также будет работать на машинах до SSE4, хотя это может не быть проблемой.


Небольшое замечание по эффективности существующего кода: вместо этого

tmp0 = _mm_add_ps(tmp0, tmp1);
tmp0 = _mm_add_ps(tmp0, tmp2);
tmp0 = _mm_add_ps(tmp0, tmp3);
tmp0 = _mm_add_ps(tmp0, C_0n);

Это может быть немного лучше сделать это:

tmp0 = _mm_add_ps(tmp0, tmp1);  // 0 + 1 -> 0
tmp2 = _mm_add_ps(tmp2, tmp3);  // 2 + 3 -> 2
tmp0 = _mm_add_ps(tmp0, tmp2);  // 0 + 2 -> 0
tmp0 = _mm_add_ps(tmp0, C_0n);

Поскольку первые два mm_add_ps теперь полностью независимы. Кроме того, я не знаю относительного времени добавления и перетасовки, но это может быть немного быстрее.


Надеюсь, это поможет.

13.11.2010

2

Также можно использовать хадд SSE3. Получилось быстрее, чем при использовании _dot_ps, в некоторых тривиальных тестах. Это возвращает 4 точечных продукта, которые можно добавить.

static inline __m128 dot_p(const __m128 x, const __m128 y[4])
{
   __m128 z[4];

   z[0] = x * y[0];
   z[1] = x * y[1];
   z[2] = x * y[2];
   z[3] = x * y[3];
   z[0] = _mm_hadd_ps(z[0], z[1]);
   z[2] = _mm_hadd_ps(z[2], z[3]);
   z[0] = _mm_hadd_ps(z[0], z[2]);

   return z[0];
}
17.12.2010

3

Вы можете попробовать оставить результат скалярного произведения в младшем слове и использовать скалярное хранилище op _mm_store_ss, чтобы сохранить одно число с плавающей запятой из каждого регистра m128 в соответствующем месте массива. Буфер хранилища Nehalem должен накапливать последовательные записи в одной и той же строке и сбрасывать их в L1 пакетами.

Профессиональный способ сделать это - транспонировать celion. Макрос MSVC _MM_TRANSPOSE4_PS выполнит транспонирование за вас. .

13.11.2010
  • Вам все равно придется добавлять старое значение (C_0n) к каждому точечному продукту перед сохранением. Все они будут независимыми, так что это может быть не слишком медленно, но не намного красивее :) 13.11.2010

  • 4

    Я понимаю, что этот вопрос устарел, но зачем вообще использовать _mm_add_ps? Замените его на:

    tmp0 = _mm_or_ps(tmp0, tmp1);
    tmp2 = _mm_or_ps(tmp2, tmp3);
    tmp0 = _mm_or_ps(tmp0, tmp2);
    

    Вероятно, вы можете скрыть часть задержки _mm_dp_ps. Первый _mm_or_ps также не ждет последних двух точечных произведений, и это (быстрая) побитовая операция. Ну наконец то:

    _mm_storeu_ps(C_2, _mm_add_ps(tmp0, C_0));
    
    22.12.2012
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

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

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

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..