Tôi đang cố chuyển đổi mã c thành mã được tối ưu hóa bằng cách sử dụng nội tại neon.Neon tương đương với nội tại SSE
Dưới đây là các mã c hoạt động trên 2 toán hạng không quá vectơ của toán hạng.
uint16_t mult_z216(uint16_t a,uint16_t b){
unsigned int c1 = a*b;
if(c1)
{
int c1h = c1 >> 16;
int c1l = c1 & 0xffff;
return (c1l - c1h + ((c1l<c1h)?1:0)) & 0xffff;
}
return (1-a-b) & 0xffff;
}
Các SEE tối ưu hóa phiên bản của hoạt động này đã được thực hiện bởi những điều sau đây:
#define MULT_Z216_SSE(a, b, c) \
t0 = _mm_or_si128 ((a), (b)); \ //Computes the bitwise OR of the 128-bit value in a and the 128-bit value in b.
(c) = _mm_mullo_epi16 ((a), (b)); \ //low 16-bits of the product of two 16-bit integers
(a) = _mm_mulhi_epu16 ((a), (b)); \ //high 16-bits of the product of two 16-bit unsigned integers
(b) = _mm_subs_epu16((c), (a)); \ //Subtracts the 8 unsigned 16-bit integers of a from the 8 unsigned 16-bit integers of c and saturates
(b) = _mm_cmpeq_epi16 ((b), C_0x0_XMM); \ //Compares the 8 signed or unsigned 16-bit integers in a and the 8 signed or unsigned 16-bit integers in b for equality. (0xFFFF or 0x0)
(b) = _mm_srli_epi16 ((b), 15); \ //shift right 16 bits
(c) = _mm_sub_epi16 ((c), (a)); \ //Subtracts the 8 signed or unsigned 16-bit integers of b from the 8 signed or unsigned 16-bit integers of a.
(a) = _mm_cmpeq_epi16 ((c), C_0x0_XMM); \ ////Compares the 8 signed or unsigned 16-bit integers in a and the 8 signed or unsigned 16-bit integers in b for equality. (0xFFFF or 0x0)
(c) = _mm_add_epi16 ((c), (b)); \ // Adds the 8 signed or unsigned 16-bit integers in a to the 8 signed or unsigned 16-bit integers in b.
t0 = _mm_and_si128 (t0, (a)); \ //Computes the bitwise AND of the 128-bit value in a and the 128-bit value in b.
(c) = _mm_sub_epi16 ((c), t0); ///Subtracts the 8 signed or unsigned 16-bit integers of b from the 8 signed or unsigned 16-bit integers of a.
Tôi đã gần như chuyển đổi này sử dụng intrinsics neon:
#define MULT_Z216_NEON(a, b, out) \
temp = vorrq_u16 (*a, *b); \
// ??
// ??
*b = vsubq_u16(*out, *a); \
*b = vceqq_u16(*out, vdupq_n_u16(0x0000)); \
*b = vshrq_n_u16(*b, 15); \
*out = vsubq_s16(*out, *a); \
*a = vceqq_s16(*c, vdupq_n_u16(0x0000)); \
*c = vaddq_s16(*c, *b); \
*temp = vandq_u16(*temp, *a); \
*out = vsubq_s16(*out, *a);
Tôi chỉ thiếu các tương đương neon của _mm_mullo_epi16 ((a), (b));
và _mm_mulhi_epu16 ((a), (b));
. Hoặc là tôi hiểu nhầm điều gì đó hoặc không có bản chất như vậy trong NEON. Nếu không có cách nào để lưu trữ các bước này bằng cách sử dụng NEONS nội tại?
UPDATE:
Tôi đã quên để nhấn mạnh các điểm sau đây: các operants của hàm là uint16x8_t vectơ NEON (mỗi phần tử là một uint16_t => số nguyên từ 0 đến 65535). Trong câu trả lời, ai đó đã đề xuất sử dụng nội tại vqdmulhq_s16()
. Việc sử dụng điều này sẽ không phù hợp với việc thực hiện đã cho bởi vì phép nhân nhân sẽ giải thích các vectơ như các giá trị đã ký và tạo ra một đầu ra sai.
Nếu bạn sẽ có giá trị> 32767 thì bạn sẽ cần phải sử dụng nhân rộng được đề xuất bên dưới (vmull_u16). Nếu bạn biết rằng tất cả các giá trị của bạn sẽ là <32768, thì bạn có thể sử dụng vqdmulhq_s16. – BitBank