Лучший способ XOR двух 128-битных значений в MSVC?

Я пытаюсь XOR 128-битного вектора инициализации с открытым текстом, как показано здесь

В Linux x86-64 gcc 12.2 есть один лайнер

*(unsigned __int128 *)( plaintext ) ^= *(unsigned __int128 *)( ivvectext );

Например, https://godbolt.org/z/sc8e66qeo

#include <stdio.h>
#include <stdint.h>

int main()
{
    uint8_t plaintext[16] = {'t','h','e','q','u','i','c','k','b','r','o','w','n','f','o','x'};
    uint8_t ivvectext[16] = {'w','a','1','2','o','b','s','q','m','v','c','s','s','q','u','w'};

    *(unsigned __int128 *)( plaintext ) ^= *(unsigned __int128 *)( ivvectext );

    for (int i = 0; i < sizeof(plaintext); i++) { printf("%02X ", (unsigned char)plaintext[i]); }

    return 0;
}

Вопрос

Какой предпочтительный метод XOR для этих 128-битных значений в MSVC?

Обновлять

Как отмечено в одном из ответов, используйте встроенный компилятор _mm_xor_si128

#include <stdint.h>
#include <immintrin.h>
#include <iostream>
#include <ios>
#include <iomanip>

int main() {

    uint8_t plaintext[16] = { 't','h','e','q','u','i','c','k','b','r','o','w','n','f','o','x' };
    uint8_t ivvectext[16] = { 'w','a','1','2','o','b','s','q','m','v','c','s','s','q','u','w' };

    __m128i plain = _mm_loadu_si128((__m128i*)plaintext);
    __m128i ivvec = _mm_loadu_si128((__m128i*)ivvectext);

    __m128i xored = _mm_xor_si128(plain, ivvec);

    uint8_t* xored_array = (uint8_t*)&xored;
    for (int i = 0; i < 16; i++) {
        std::cout << std::uppercase << std::setw(2) << std::setfill('0') << std::hex << (int)xored_array[i] << " ";
    }
    std::cout << std::endl;

    return 0;
}

Вывод соответствует линукс

03 09 54 43 1A 0B 10 1A 0F 04 0C 04 1D 17 1A 0F 

Однако другие ответы предлагают более читаемый код

for (int i = 0; i < sizeof(plaintext); i++) 
{
    plaintext[i] ^= ivvectext[i];
}

и позвольте оптимизации компилятора разобраться во внутреннем ассемблерном коде. :)

Взгляните на XORintrinsics

user20716902 07.02.2023 15:32

Почему это помечено C и C++? Выберите один язык.

Shawn 07.02.2023 15:39
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
94
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Как упоминалось в моем комментарии, я бы использовал intrincis: вот как это сделать в MSVC:

#include <immintrin.h>
int main() {

    uint8_t plaintext[16] = { 't','h','e','q','u','i','c','k','b','r','o','w','n','f','o','x' };
    uint8_t ivvectext[16] = { 'w','a','1','2','o','b','s','q','m','v','c','s','s','q','u','w' };

    __m128i plain = _mm_loadu_si128((__m128i*)plaintext);
    __m128i ivvec = _mm_loadu_si128((__m128i*)ivvectext);

    __m128i xored = _mm_xor_si128(plain, ivvec);
    
    return 0;
}
Ответ принят как подходящий

Если ваша цель — оптимизировать код, оставьте эту задачу компилятору. (Конечно, вам, возможно, придется включить оптимизацию.)

Вы можете написать простой цикл, например

for (int i = 0; i < sizeof(plaintext); i++) 
{
    plaintext[i] ^= ivvectext[i];
}

и пусть компилятор оптимизирует это.

Например, x86 msvc v19.latest с параметром -O2 создает инструкции SSE2 из этого цикла, что приводит к одной 128-битной операции.

_main   PROC                                      ; COMDAT
        sub     esp, 36                             ; 00000024H
        mov     eax, DWORD PTR ___security_cookie
        xor     eax, esp
        mov     DWORD PTR __$ArrayPad$[esp+36], eax
        mov     DWORD PTR _plaintext$[esp+36], 1902471284 ; 71656874H
        mov     DWORD PTR _plaintext$[esp+40], 1801677173 ; 6b636975H
        mov     DWORD PTR _plaintext$[esp+44], 2003792482 ; 776f7262H
        mov     DWORD PTR _plaintext$[esp+48], 2020566638 ; 786f666eH
        movups  xmm1, XMMWORD PTR _plaintext$[esp+36]
        mov     DWORD PTR _ivvectext$[esp+36], 842097015 ; 32316177H
        mov     DWORD PTR _ivvectext$[esp+40], 1903387247 ; 7173626fH
        mov     DWORD PTR _ivvectext$[esp+44], 1935898221 ; 7363766dH
        mov     DWORD PTR _ivvectext$[esp+48], 2004185459 ; 77757173H
        movups  xmm0, XMMWORD PTR _ivvectext$[esp+36]
        push    esi
        xor     esi, esi
        pxor    xmm1, xmm0
        movups  XMMWORD PTR _plaintext$[esp+40], xmm1
...

Смотрите https://godbolt.org/z/afTPK5von

Дополнительные подсказки из комментариев:

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

Иногда компилятор MS не оптимизирует то, что выглядит как простой цикл, в этом случае вы можете включить сообщения векторизатора и параллелизатора , которые могут дать вам подсказки, почему это не так. ( комментарий пользователя 20716902)

Мне нравится это решение. Это НАМНОГО более читабельно, чем использование встроенного! Напишите это просто и позвольте оптимизации компилятора разобраться во внутреннем ассемблерном коде. :)

vengy 07.02.2023 16:05

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

Eljay 07.02.2023 16:39

Иногда компилятор MS не оптимизирует то, что выглядит как простой цикл, в этом случае вы можете включить сообщения Vectorizer и parallilizer, которые могут дать вам подсказки, почему это не так.

user20716902 07.02.2023 16:49

Другие вопросы по теме