Можно ли оптимизировать код? - Assembler

Узнай цену своей работы

Формулировка задачи:

День добрый! Задача: есть полубайтовые значения - машина выдает данные от 1 до 15. Хранятся и передаются они в упакованном виде, по два значения в байте, в виде массива байт. Т.е. ar[i]:= value[i*2] + 16*value[i*2+1]. Данных много, поиск по ним - наиболее часто требуемая функция, поэтому хочется написать её на ассемблере (основной пакет - на Delphi). Задача гоняется в 64-битной среде, поэтому функция написана для x64. Привожу функцию поиска для трёх идущих подряд значений. Писал сам Наверняка можно оптимизировать, приоритет - оптимизация по времени исполнения. Взгляните, пожалуйста, ткните носом, где можно ускорить. Может, можно использовать, например, SIMD для одновременного поиска двух подряд масок?
type
  ByteArray = array of byte;
 
function Search4Word(ba:ByteArray; mask:word):int64;  // mask = $F000 + требуемые нам упакованные три величины
asm
   mov bx, dx // дублируем маску
   rol dx, 4  // и сразу же её сдвигаем
   xor r10,r10 // счётчик
   mov r9,[rcx-8] // сохраняем количество байтов в байтовом массиве
   dec r9 //мы сравниваем байты со словом с шагом в один байт, таких сравнений будет length - 1, поэтому сразу уменьшаем счётчик на 1
@loop:
   mov ax, [rcx]  // загрузка байта
   push ax  // сохраняем для ещё одной проверки
   or ax, $F000 // накладываем маску
   cmp bx, ax // сравниваем
   pop ax // восстанавливаем AX
   jne @not_found
   shl r10, 1 // Нашли! Умножаем на 2, указывая адрес в "полубайтах"
   mov rax, r10
   ret
@not_found:
   or ax, $000F
   cmp dx, ax
   jne @not_found_ror
   shl r10, 1 // Нашли! Умножаем на 2, указывая адрес в "полубайтах"
   inc r10  // и увеличиваем на 1 - был сдвиг
   mov rax, r10
   ret
@not_found_ror:  // Не нашли, циклим
   inc rcx
   inc r10
   cmp r10, r9
   jne @loop
   mov rax, - 1 // возвращаем "-1": ничего не нашли
   ret
end;

Решение задачи: «Можно ли оптимизировать код?»

textual
Листинг программы
#include "stdafx.h"
#include <nmmintrin.h>
 
 
int main()
{
    __m128i a = _mm_load_si128((__m128i*)"456             ");
    __m128i b = _mm_load_si128((__m128i*)"ssss456yyyyyyyyy");   
    int result = _mm_cmpestri(a, 3, b, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ORDERED); //ответ 4
    return 0;
}

Объяснение кода листинга программы

  1. Подключение необходимых заголовочных файлов.
  2. Объявление функции main.
  3. Загрузка первых 4 байт из памяти в переменную a.
  4. Загрузка следующих 16 байт из памяти в переменную b.
  5. Выполнение сравнения двух строк с использованием SSE инструкции _mm_cmpestri.
  6. Результат сравнения сохраняется в переменной result.
  7. Возврат 0 из функции main, что означает успешный конец работы программы.

Оцени полезность:

9   голосов , оценка 3.667 из 5