NumPy 源码解析(五十五)
.\numpy\numpy\_core\src\common\simd\avx2\operators.h
/***************************
* Shifting
***************************/
// 定义 AVX2 下的无符号 16 位整数左移操作,参数 A 为输入向量,C 为移位常数
// 定义 AVX2 下的有符号 16 位整数左移操作,参数 A 为输入向量,C 为移位常数
// 定义 AVX2 下的无符号 32 位整数左移操作,参数 A 为输入向量,C 为移位常数
// 定义 AVX2 下的有符号 32 位整数左移操作,参数 A 为输入向量,C 为移位常数
// 定义 AVX2 下的无符号 64 位整数左移操作,参数 A 为输入向量,C 为移位常数
// 定义 AVX2 下的有符号 64 位整数左移操作,参数 A 为输入向量,C 为移位常数
// 左移动作,移位常数为立即数
// 定义 AVX2 下的无符号 16 位整数右移操作,参数 A 为输入向量,C 为移位常数
// 定义 AVX2 下的有符号 16 位整数右移操作,参数 A 为输入向量,C 为移位常数
// 定义 AVX2 下的无符号 32 位整数右移操作,参数 A 为输入向量,C 为移位常数
// 定义 AVX2 下的有符号 32 位整数右移操作,参数 A 为输入向量,C 为移位常数
// 定义 AVX2 下的无符号 64 位整数右移操作,参数 A 为输入向量,C 为移位常数
// 定义 AVX2 下的有符号 64 位整数右移操作,参数 A 为输入向量,C 为移位常数
NPY_FINLINE __m256i npyv_shr_s64(__m256i a, int c)
{
// 定义常量 sbit 为 0x8000000000000000 的 256 位整数向量
const __m256i sbit = _mm256_set1_epi64x(0x8000000000000000);
// 将整数常数 c 转换为 128 位整数向量
const __m128i c64 = _mm_cvtsi32_si128(c);
// 计算右移结果,包括符号位的扩展
__m256i r = _mm256_srl_epi64(_mm256_add_epi64(a, sbit), c64);
// 还原符号位
return _mm256_sub_epi64(r, _mm256_srl_epi64(sbit, c64));
}
// 右移动作,移位常数为立即数
/***************************
* Logical
***************************/
// 逻辑 AND 操作,参数类型为无符号 8 位整数
// 逻辑 AND 操作,参数类型为有符号 8 位整数
// 逻辑 AND 操作,参数类型为无符号 16 位整数
// 逻辑 AND 操作,参数类型为有符号 16 位整数
// 逻辑 AND 操作,参数类型为无符号 32 位整数
// 逻辑 AND 操作,参数类型为有符号 32 位整数
// 逻辑 AND 操作,参数类型为无符号 64 位整数
// 逻辑 AND 操作,参数类型为有符号 64 位整数
// 逻辑 AND 操作,参数类型为单精度浮点数
// 逻辑 AND 操作,参数类型为双精度浮点数
// 逻辑 AND 操作,参数类型为布尔值 8 位
// 逻辑 AND 操作,参数类型为布尔值 16 位
// 逻辑 AND 操作,参数类型为布尔值 32 位
// 逻辑 AND 操作,参数类型为布尔值 64 位
// 逻辑 OR 操作,参数类型为无符号 8 位整数
// 逻辑 OR 操作,参数类型为有符号 8 位整数
// 逻辑 OR 操作,参数类型为无符号 16 位整数
// 逻辑 OR 操作,参数类型为有符号 16 位整数
// 逻辑 OR 操作,参数类型为无符号 32 位整数
// 逻辑 OR 操作,参数类型为有符号 32 位整数
// 逻辑 OR 操作,参数类型为无符号 64 位整数
// 逻辑 OR 操作
// 定义按位异或操作,用于不同数据类型的操作
// NOT 操作的宏定义
// ANDC, ORC 和 XNOR 操作的宏定义
/***************************
* Comparison
***************************/
// 整数相等比较操作的宏定义
// 整数不相等比较操作的宏定义
// 有符号大于比较操作的宏定义
// 有符号大于等于比较操作的宏定义
// 以下是一个未完成的宏定义,用于无符号大于比较操作
NPY_FINLINE __m256i npyv_cmpgt_u
{ \
const __m256i sbit = _mm256_set1_epi32(SIGN); \
return _mm256_cmpgt_epi
_mm256_xor_si256(a, sbit), _mm256_xor_si256(b, sbit) \
); \
}
// 使用 AVX2 指令集实现无符号大于比较操作,对每个操作数为8位整数的向量进行比较
NPYV_IMPL_AVX2_UNSIGNED_GT(8, 0x80808080)
// 使用 AVX2 指令集实现无符号大于比较操作,对每个操作数为16位整数的向量进行比较
NPYV_IMPL_AVX2_UNSIGNED_GT(16, 0x80008000)
// 使用 AVX2 指令集实现无符号大于比较操作,对每个操作数为32位整数的向量进行比较
NPYV_IMPL_AVX2_UNSIGNED_GT(32, 0x80000000)
// 定义函数 npyv_cmpgt_u64,用于比较两个64位整数向量 a 和 b 的大于关系
NPY_FINLINE __m256i npyv_cmpgt_u64(__m256i a, __m256i b)
{
// 定义掩码向量 sbit,用于对比特反转,用于处理符号位的比较
const __m256i sbit = _mm256_set1_epi64x(0x8000000000000000);
// 返回对 a 和 b 的符号反转后的比较结果
return _mm256_cmpgt_epi64(_mm256_xor_si256(a, sbit), _mm256_xor_si256(b, sbit));
}
// 定义函数 npyv_cmpge_u8,用于比较两个8位无符号整数向量 a 和 b 的大于等于关系
NPY_FINLINE __m256i npyv_cmpge_u8(__m256i a, __m256i b)
{ return _mm256_cmpeq_epi8(a, _mm256_max_epu8(a, b)); }
// 定义函数 npyv_cmpge_u16,用于比较两个16位无符号整数向量 a 和 b 的大于等于关系
NPY_FINLINE __m256i npyv_cmpge_u16(__m256i a, __m256i b)
{ return _mm256_cmpeq_epi16(a, _mm256_max_epu16(a, b)); }
// 定义函数 npyv_cmpge_u32,用于比较两个32位无符号整数向量 a 和 b 的大于等于关系
NPY_FINLINE __m256i npyv_cmpge_u32(__m256i a, __m256i b)
{ return _mm256_cmpeq_epi32(a, _mm256_max_epu32(a, b)); }
// 定义宏 npyv_cmpge_u64,用于比较两个64位无符号整数向量 A 和 B 的大于等于关系
// 定义一系列宏,用于实现不同类型的小于比较操作,分别对应不同的整数类型(有符号和无符号)
// 定义一系列宏,用于实现不同类型的小于等于比较操作,分别对应不同的整数类型(有符号和无符号)
// 定义一系列宏,用于实现浮点数的精确比较操作,包括等于、不等于、小于、小于等于、大于、大于等于
// 定义函数 npyv_notnan_f32,用于检查32位浮点数向量 a 中的元素是否都不是 NaN
NPY_FINLINE npyv_b32 npyv_notnan_f32(npyv_f32 a)
{ return _mm256_castps_si256(_mm256_cmp_ps(a, a, _CMP_ORD_Q)); }
// 定义函数 npyv_notnan_f64,用于检查64位浮点数向量 a 中的元素是否都不是 NaN
NPY_FINLINE npyv_b64 npyv_notnan_f64(npyv_f64 a)
{ return _mm256_castpd_si256(_mm256_cmp_pd(a, a, _CMP_ORD_Q)); }
// 测试跨所有向量通道的情况
// any: 如果任意一个元素不等于零,则返回 true
// 定义宏 NPYV_IMPL_AVX2_ANYALL(SFX),用于生成 AVX2 指令集的任意/全部判断函数
// 内联函数,判断 AVX2 数据类型为 SFX 的向量是否存在非零元素
NPY_FINLINE bool npyv_any_
{ \
// 使用 AVX2 指令 movemask 判断向量是否存在不等于零的元素
return _mm256_movemask_epi8( \
npyv_cmpeq_
) != -1; \
} \
// 内联函数,判断 AVX2 数据类型为 SFX 的向量是否所有元素都等于零
NPY_FINLINE bool npyv_all_
{ \
// 使用 AVX2 指令 movemask 判断向量是否所有元素都等于零
return _mm256_movemask_epi8( \
npyv_cmpeq_
) == 0; \
}
// 生成 AVX2 指令集的不同数据类型的任意/全部判断函数
NPYV_IMPL_AVX2_ANYALL(b8)
NPYV_IMPL_AVX2_ANYALL(b16)
NPYV_IMPL_AVX2_ANYALL(b32)
NPYV_IMPL_AVX2_ANYALL(b64)
// 取消定义宏 NPYV_IMPL_AVX2_ANYALL,避免宏定义冲突
// 重新定义宏 NPYV_IMPL_AVX2_ANYALL(SFX),用于生成 AVX2 指令集的任意/全部判断函数
// 内联函数,判断 AVX2 数据类型为 SFX 的向量是否存在非零元素 \
NPY_FINLINE bool npyv_any_
{ \
// 使用 AVX2 指令 movemask 判断向量是否存在不等于零的元素 \
return _mm256_movemask_
_mm256_cmp_
) != MASK; \
} \
// 内联函数,判断 AVX2 数据类型为 SFX 的向量是否所有元素都等于零 \
NPY_FINLINE bool npyv_all_
{ \
// 使用 AVX2 指令 movemask 判断向量是否所有元素都等于零 \
return _mm256_movemask_
_mm256_cmp_
) == 0; \
}
// 生成 AVX2 指令集的不同数据类型的任意/全部判断函数,使用不同的比较器 XSFX 和 MASK
NPYV_IMPL_AVX2_ANYALL(u8, ps, 0xff)
NPYV_IMPL_AVX2_ANYALL(s8, ps, 0xff)
NPYV_IMPL_AVX2_ANYALL(u16, ps, 0xff)
NPYV_IMPL_AVX2_ANYALL(s16, ps, 0xff)
NPYV_IMPL_AVX2_ANYALL(u32, ps, 0xff)
NPYV_IMPL_AVX2_ANYALL(s32, ps, 0xff)
NPYV_IMPL_AVX2_ANYALL(u64, ps, 0xff)
NPYV_IMPL_AVX2_ANYALL(s64, ps, 0xff)
NPYV_IMPL_AVX2_ANYALL(f32, pd, 0xf)
NPYV_IMPL_AVX2_ANYALL(f64, pd, 0xf)
// 取消定义宏 NPYV_IMPL_AVX2_ANYALL,避免宏定义冲突
// 结束条件,关闭头文件 _NPY_SIMD_AVX2_OPERATORS_H 的声明
.\numpy\numpy\_core\src\common\simd\avx2\reorder.h
// 定义宏:将两个向量的低部分组合起来
// 定义宏:将两个向量的高部分组合起来
// 定义函数:将两个__m256i类型的向量a和b的低高部分分别组合起来,返回一个npyv_m256ix2结构体
NPY_FINLINE npyv_m256ix2 npyv__combine(__m256i a, __m256i b)
{
npyv_m256ix2 r;
// 将a和b的低高部分组合成a1b0
__m256i a1b0 = _mm256_permute2x128_si256(a, b, 0x21);
// 使用_blend_epi32混合a和a1b0的高低部分,将结果存入r.val[0]
r.val[0] = _mm256_blend_epi32(a, a1b0, 0xF0);
// 使用_blend_epi32混合b和a1b0的高低部分,将结果存入r.val[1]
r.val[1] = _mm256_blend_epi32(b, a1b0, 0xF);
return r;
}
// 定义函数:将两个__m256类型的向量a和b的低高部分分别组合起来,返回一个npyv_f32x2结构体
NPY_FINLINE npyv_f32x2 npyv_combine_f32(__m256 a, __m256 b)
{
npyv_f32x2 r;
// 将a和b的低高部分组合成a1b0
__m256 a1b0 = _mm256_permute2f128_ps(a, b, 0x21);
// 使用_blend_ps混合a和a1b0的高低部分,将结果存入r.val[0]
r.val[0] = _mm256_blend_ps(a, a1b0, 0xF0);
// 使用_blend_ps混合b和a1b0的高低部分,将结果存入r.val[1]
r.val[1] = _mm256_blend_ps(b, a1b0, 0xF);
return r;
}
// 定义函数:将两个__m256d类型的向量a和b的低高部分分别组合起来,返回一个npyv_f64x2结构体
NPY_FINLINE npyv_f64x2 npyv_combine_f64(__m256d a, __m256d b)
{
npyv_f64x2 r;
// 将a和b的低高部分组合成a1b0
__m256d a1b0 = _mm256_permute2f128_pd(a, b, 0x21);
// 使用_blend_pd混合a和a1b0的高低部分,将结果存入r.val[0]
r.val[0] = _mm256_blend_pd(a, a1b0, 0xC);
// 使用_blend_pd混合b和a1b0的高低部分,将结果存入r.val[1]
r.val[1] = _mm256_blend_pd(b, a1b0, 0x3);
return r;
}
// 定义宏:实现AVX2下的ZIP操作,将向量a和b的低高部分交织在一起,返回一个T_VEC
NPY_FINLINE T_VEC
{ \
// 将向量a和b的低部分和高部分分别解包并交织在一起,得到ab0和ab1
__m256i ab0 = _mm256_unpacklo_epi
__m256i ab1 = _mm256_unpackhi_epi
// 使用npyv__combine将ab0和ab1的低高部分组合在一起,返回结果
return npyv__combine(ab0, ab1); \
}
// 为不同长度的T_VEC类型定义具体的ZIP函数实现
NPYV_IMPL_AVX2_ZIP_U(npyv_u8, 8)
NPYV_IMPL_AVX2_ZIP_U(npyv_u16, 16)
NPYV_IMPL_AVX2_ZIP_U(npyv_u32, 32)
NPYV_IMPL_AVX2_ZIP_U(npyv_u64, 64)
// 定义一个宏,将有符号64位整数向无符号64位整数的转换别名为npv_zip_s64
// 定义一个内联函数,将两个256位单精度浮点数向量a和b解交错
NPY_FINLINE npyv_f32x2 npyv_zip_f32(__m256 a, __m256 b)
{
// 解交错操作,取出a和b的低128位和高128位,分别放入ab0和ab1
__m256 ab0 = _mm256_unpacklo_ps(a, b);
__m256 ab1 = _mm256_unpackhi_ps(a, b);
// 调用npv_combine_f32函数将ab0和ab1组合成一个新的256位单精度浮点数向量
return npyv_combine_f32(ab0, ab1);
}
// 定义一个内联函数,将两个256位双精度浮点数向量a和b解交错
NPY_FINLINE npyv_f64x2 npyv_zip_f64(__m256d a, __m256d b)
{
// 解交错操作,取出a和b的低128位和高128位,分别放入ab0和ab1
__m256d ab0 = _mm256_unpacklo_pd(a, b);
__m256d ab1 = _mm256_unpackhi_pd(a, b);
// 调用npv_combine_f64函数将ab0和ab1组合成一个新的256位双精度浮点数向量
return npyv_combine_f64(ab0, ab1);
}
// 定义一个内联函数,将两个8位无符号整数向量ab0和ab1进行解插值
NPY_FINLINE npyv_u8x2 npyv_unzip_u8(npyv_u8 ab0, npyv_u8 ab1)
{
// 创建一个常量256位整数向量idx,其排列顺序按照指定的索引
const __m256i idx = _mm256_setr_epi8(
0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15,
0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15
);
// 对ab0和ab1按照idx的顺序进行字节级重排
__m256i ab_03 = _mm256_shuffle_epi8(ab0, idx);
__m256i ab_12 = _mm256_shuffle_epi8(ab1, idx);
// 调用npyv_combine_u8将ab_03和ab_12组合成一个新的8位无符号整数向量对
npyv_u8x2 ab_lh = npyv_combine_u8(ab_03, ab_12);
npyv_u8x2 r;
// 将ab_lh的val[0]和val[1]分别按64位整数进行解交错,存入r.val[0]和r.val[1]
r.val[0] = _mm256_unpacklo_epi64(ab_lh.val[0], ab_lh.val[1]);
r.val[1] = _mm256_unpackhi_epi64(ab_lh.val[0], ab_lh.val[1]);
return r;
}
// 定义一个宏,将有符号8位整数向无符号8位整数的转换别名为npv_unzip_s8
// 定义一个内联函数,将两个16位无符号整数向量ab0和ab1进行解插值
NPY_FINLINE npyv_u16x2 npyv_unzip_u16(npyv_u16 ab0, npyv_u16 ab1)
{
// 创建一个常量256位整数向量idx,其排列顺序按照指定的索引
const __m256i idx = _mm256_setr_epi8(
0,1, 4,5, 8,9, 12,13, 2,3, 6,7, 10,11, 14,15,
0,1, 4,5, 8,9, 12,13, 2,3, 6,7, 10,11, 14,15
);
// 对ab0和ab1按照idx的顺序进行字节级重排
__m256i ab_03 = _mm256_shuffle_epi8(ab0, idx);
__m256i ab_12 = _mm256_shuffle_epi8(ab1, idx);
// 调用npyv_combine_u16将ab_03和ab_12组合成一个新的16位无符号整数向量对
npyv_u16x2 ab_lh = npyv_combine_u16(ab_03, ab_12);
npyv_u16x2 r;
// 将ab_lh的val[0]和val[1]分别按64位整数进行解交错,存入r.val[0]和r.val[1]
r.val[0] = _mm256_unpacklo_epi64(ab_lh.val[0], ab_lh.val[1]);
r.val[1] = _mm256_unpackhi_epi64(ab_lh.val[0], ab_lh.val[1]);
return r;
}
// 定义一个宏,将有符号16位整数向无符号16位整数的转换别名为npv_unzip_s16
// 定义一个内联函数,将两个32位无符号整数向量ab0和ab1进行解插值
NPY_FINLINE npyv_u32x2 npyv_unzip_u32(npyv_u32 ab0, npyv_u32 ab1)
{
// 创建一个常量256位整数向量idx,按照指定的索引对ab0和ab1进行32位整数级的重排
const __m256i idx = npyv_set_u32(0, 2, 4, 6, 1, 3, 5, 7);
__m256i abl = _mm256_permutevar8x32_epi32(ab0, idx);
__m256i abh = _mm256_permutevar8x32_epi32(ab1, idx);
// 调用npyv_combine_u32将abl和abh组合成一个新的32位无符号整数向量对
return npyv_combine_u32(abl, abh);
}
// 定义一个宏,将有符号32位整数向无符号32位整数的转换别名为npv_unzip_s32
// 定义一个内联函数,将两个64位无符号整数向量ab0和ab1进行解插值
NPY_FINLINE npyv_u64x2 npyv_unzip_u64(npyv_u64 ab0, npyv_u64 ab1)
{
// 调用npyv_combine_u64将ab0和ab1组合成一个新的64位无符号整数向量对
npyv_u64x2 ab_lh = npyv_combine_u64(ab0, ab1);
npyv_u64x2 r;
// 将ab_lh的val[0]和val[1]分别按64位整数进行解交错,存入r.val[0]和r.val[1]
r.val[0] = _mm256_unpacklo_epi64(ab_lh.val[0], ab_lh.val[1]);
r.val[1] = _mm256_unpackhi_epi64(ab_lh.val[0], ab_lh.val[1]);
return r;
}
// 定义一个宏,将有符号64位整数向无符号64位整数的转换别名为npv_unzip_s64
// 定义一个内联函数,将两个32位单精度浮点数向量ab0和ab1进行解插值
NPY_FINLINE npyv_f32x2 npyv_unzip_f32(npyv_f32 ab
const __m256i idx = _mm256_setr_epi8(
7, 6, 5, 4, 3, 2, 1, 0,/*64*/15, 14, 13, 12, 11, 10, 9, 8,
7, 6, 5, 4, 3, 2, 1, 0,/*64*/15, 14, 13, 12, 11, 10, 9, 8
);
return _mm256_shuffle_epi8(a, idx);
// 定义宏 npyv_rev64_s8,将其映射到 npyv_rev64_u8 宏
// 定义函数 npyv_rev64_u16,反转参数中的 16 位元素顺序
NPY_FINLINE npyv_u16 npyv_rev64_u16(npyv_u16 a)
{
// 创建一个常量 __m256i 类型的索引,用于反转 16 位元素的顺序
const __m256i idx = _mm256_setr_epi8(
6, 7, 4, 5, 2, 3, 0, 1,/*64*/14, 15, 12, 13, 10, 11, 8, 9,
6, 7, 4, 5, 2, 3, 0, 1,/*64*/14, 15, 12, 13, 10, 11, 8, 9
);
// 使用 AVX2 指令集中的 _mm256_shuffle_epi8 对 a 应用 idx 索引进行元素反转
return _mm256_shuffle_epi8(a, idx);
}
// 定义宏 npyv_rev64_s16,将其映射到 npyv_rev64_u16 宏
// 定义函数 npyv_rev64_u32,反转参数中的 32 位元素顺序
NPY_FINLINE npyv_u32 npyv_rev64_u32(npyv_u32 a)
{
// 使用 AVX2 指令集中的 _mm256_shuffle_epi32 对 a 进行元素反转,模式为 _MM_SHUFFLE(2, 3, 0, 1)
return _mm256_shuffle_epi32(a, _MM_SHUFFLE(2, 3, 0, 1));
}
// 定义宏 npyv_rev64_s32,将其映射到 npyv_rev64_u32 宏
// 定义函数 npyv_rev64_f32,反转参数中的 32 位浮点元素顺序
NPY_FINLINE npyv_f32 npyv_rev64_f32(npyv_f32 a)
{
// 使用 AVX 指令集中的 _mm256_shuffle_ps 对 a 进行浮点元素反转,模式为 _MM_SHUFFLE(2, 3, 0, 1)
return _mm256_shuffle_ps(a, a, _MM_SHUFFLE(2, 3, 0, 1));
}
// Permuting the elements of each 128-bit lane by immediate index for
// each element.
// 定义宏 npyv_permi128_u32,根据指定的索引对 128 位数据进行元素排列
// 使用 AVX2 指令集中的 _mm256_shuffle_epi32 对 A 进行元素排列,模式由参数 E0, E1, E2, E3 确定
// 定义宏 npyv_permi128_s32,将其映射到 npyv_permi128_u32 宏
// 定义宏 npyv_permi128_u64,根据指定的索引对 128 位数据进行元素排列
// 使用 AVX2 指令集中的 _mm256_shuffle_epi32 对 A 进行元素排列,模式由参数 E0, E1 确定
// 定义宏 npyv_permi128_s64,将其映射到 npyv_permi128_u64 宏
// 定义宏 npyv_permi128_f32,根据指定的索引对 128 位数据进行浮点元素排列
// 使用 AVX 指令集中的 _mm256_permute_ps 对 A 进行浮点元素排列,模式由参数 E0, E1, E2, E3 确定
// 定义宏 npyv_permi128_f64,根据指定的索引对 128 位数据进行双精度浮点元素排列
// 使用 AVX 指令集中的 _mm256_permute_pd 对 A 进行双精度浮点元素排列,模式由参数 E0, E1 确定
// 结束条件,结束条件编译器的处理,确保头文件内容不会被重复包含
.\numpy\numpy\_core\src\common\simd\avx2\utils.h
// 定义一个宏,用于对256位整数型寄存器进行奇数位元素的重新排列
// 定义一个宏,将256位浮点型寄存器转换为按奇数位元素排列的浮点型寄存器
// 定义一个宏,用于对256位双精度浮点型寄存器进行奇数位元素的重新排列
// 定义一个内联函数,实现无符号8位整数型寄存器的乘法操作
NPY_FINLINE __m256i npyv256_mul_u8(__m256i a, __m256i b)
{
// 创建一个掩码,用于选择奇数位元素
const __m256i mask = _mm256_set1_epi32(0xFF00FF00);
// 计算偶数位元素乘积
__m256i even = _mm256_mullo_epi16(a, b);
// 计算奇数位元素乘积
__m256i odd = _mm256_mullo_epi16(_mm256_srai_epi16(a, 8), _mm256_srai_epi16(b, 8));
// 将奇数位元素乘积左移8位
odd = _mm256_slli_epi16(odd, 8);
// 使用掩码选择奇数位元素乘积或偶数位元素乘积
return _mm256_blendv_epi8(even, odd, mask);
}
.\numpy\numpy\_core\src\common\simd\avx512\arithmetic.h
/***************************
* Addition
***************************/
// 定义 AVX512BW 指令集下的非饱和加法操作宏
// 如果不支持 AVX512BW,则从 AVX2 中实现 AVX512 指令集的非饱和加法操作宏
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_add_u8, _mm256_add_epi8)
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_add_u16, _mm256_add_epi16)
// 定义有符号整数类型的非饱和加法宏
// 定义 AVX512BW 指令集下的饱和加法操作宏
// 如果不支持 AVX512BW,则从 AVX2 中实现 AVX512 指令集的饱和加法操作宏
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_adds_u8, _mm256_adds_epu8)
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_adds_s8, _mm256_adds_epi8)
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_adds_u16, _mm256_adds_epu16)
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_adds_s16, _mm256_adds_epi16)
// TODO: rest, after implement Packs intrins
/***************************
* Subtraction
***************************/
// 定义 AVX512BW 指令集下的非饱和减法操作宏
// 如果不支持 AVX512BW,则从 AVX2 中实现 AVX512 指令集的非饱和减法操作宏
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_sub_u8, _mm256_sub_epi8)
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_sub_u16, _mm256_sub_epi16)
// 定义有符号整数类型的非饱和减法宏
// 定义 AVX512BW 指令集下的饱和减法操作宏
// 如果不支持 AVX512BW,则从 AVX2 中实现 AVX512 指令集的饱和减法操作宏
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_subs_u8, _mm256_subs_epu8)
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_subs_s8, _mm256_subs_epi8)
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_subs_u16, _mm256_subs_epu16)
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_subs_s16, _mm256_subs_epi16)
// TODO: rest, after implement Packs intrins
/***************************
* Multiplication
***************************/
// 定义 AVX512BW 指令集下的非饱和乘法函数实现
NPY_FINLINE __m512i npyv_mul_u8(__m512i a, __m512i b)
{
// 计算偶数位置的乘积
__m512i even = _mm512_mullo_epi16(a, b);
// 计算奇数位置的乘积
__m512i odd = _mm512_mullo_epi16(_mm512_srai_epi16(a, 8), _mm512_srai_epi16(b, 8));
odd = _mm512_slli_epi16(odd, 8);
// 合并偶数和奇数位置的结果,构成完整的乘积结果
return _mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, even, odd);
}
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_mul_u8, npyv256_mul_u8)
// 如果支持 AVX-512 BW 指令集,使用 AVX-512 指令进行无符号 16 位整数乘法
// 如果不支持 AVX-512 BW 指令集,将 AVX2 的 256 位整数乘法扩展为 AVX-512
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_mul_u16, _mm256_mullo_epi16)
// 将有符号 8 位整数乘法定义为无符号 8 位整数乘法
// 将有符号 16 位整数乘法定义为无符号 16 位整数乘法
// 使用 AVX-512 指令进行无符号 32 位整数乘法
// 使用 AVX-512 指令进行有符号 32 位整数乘法
// 使用 AVX-512 指令进行单精度浮点数乘法
// 使用 AVX-512 指令进行双精度浮点数乘法
// saturated
// 饱和运算
// TODO: 实现 Packs 指令之后完成此部分
/***************************
* Integer Division
***************************/
// 整数除法
// 详细内容请参见 simd/intdiv.h
// divide each unsigned 8-bit element by divisor
// 将每个无符号 8 位元素除以除数
NPY_FINLINE npyv_u8 npyv_divc_u8(npyv_u8 a, const npyv_u8x3 divisor)
{
// 从 divisor 中提取需要的位移量
const __m128i shf1 = _mm512_castsi512_si128(divisor.val[1]);
const __m128i shf2 = _mm512_castsi512_si128(divisor.val[2]);
// 如果支持 AVX-512 BW 指令集
const __m512i bmask = _mm512_set1_epi32(0x00FF00FF);
// 计算位移的掩码
const __m512i shf1b = _mm512_set1_epi8(0xFFU >> _mm_cvtsi128_si32(shf1));
const __m512i shf2b = _mm512_set1_epi8(0xFFU >> _mm_cvtsi128_si32(shf2));
// 对偶数位置进行乘法
__m512i mulhi_even = _mm512_mullo_epi16(_mm512_and_si512(a, bmask), divisor.val[0]);
mulhi_even = _mm512_srli_epi16(mulhi_even, 8);
// 对奇数位置进行乘法
__m512i mulhi_odd = _mm512_mullo_epi16(_mm512_srli_epi16(a, 8), divisor.val[0]);
// 将奇偶结果合并
__m512i mulhi = _mm512_mask_mov_epi8(mulhi_even, 0xAAAAAAAAAAAAAAAA, mulhi_odd);
// 计算商 q = floor(a/d)
__m512i q = _mm512_sub_epi8(a, mulhi);
q = _mm512_and_si512(_mm512_srl_epi16(q, shf1), shf1b);
q = _mm512_add_epi8(mulhi, q);
q = _mm512_and_si512(_mm512_srl_epi16(q, shf2), shf2b);
return q;
// 如果不支持 AVX-512 BW 指令集
const __m256i bmask = _mm256_set1_epi32(0x00FF00FF);
const __m256i shf1b = _mm256_set1_epi8(0xFFU >> _mm_cvtsi128_si32(shf1));
const __m256i shf2b = _mm256_set1_epi8(0xFFU >> _mm_cvtsi128_si32(shf2));
const __m512i shf2bw= npyv512_combine_si256(shf2b, shf2b);
const __m256i mulc = npyv512_lower_si256(divisor.val[0]);
// 从 a 中获取低 256 位
__m256i lo_a = npyv512_lower_si256(a);
// 对偶数位置进行乘法
__m256i mulhi_even = _mm256_mullo_epi16(_mm256_and_si256(lo_a, bmask), mulc);
mulhi_even = _mm256_srli_epi16(mulhi_even, 8);
// 对奇数位置进行乘法
__m256i mulhi_odd = _mm256_mullo_epi16(_mm256_srli_epi16(lo_a, 8), mulc);
// 将奇偶结果合并
__m256i mulhi = _mm256_blendv_epi8(mulhi_odd, mulhi_even, bmask);
// 计算低位结果
__m256i lo_q = _mm256_sub_epi8(lo_a, mulhi);
lo_q = _mm256_and_si256(_mm256_srl_epi16(lo_q, shf1), shf1b);
lo_q = _mm256_add_epi8(mulhi, lo_q);
lo_q = _mm256_srl_epi16(lo_q, shf2);
// 继续处理高 256 位
__m256i hi_a = npyv512_higher_si256(a);
// ...
// 计算无符号乘法的高位部分
__m256i mulhi_even = _mm256_mullo_epi16(_mm256_and_si256(hi_a, bmask), mulc);
mulhi_even = _mm256_srli_epi16(mulhi_even, 8);
__m256i mulhi_odd = _mm256_mullo_epi16(_mm256_srli_epi16(hi_a, 8), mulc);
// 选择偶数位和奇数位的乘积结果
__m256i mulhi = _mm256_blendv_epi8(mulhi_odd, mulhi_even, bmask);
// 计算 floor(a/d) = (mulhi + ((a - mulhi) >> sh1)) >> sh2
__m256i hi_q = _mm256_sub_epi8(hi_a, mulhi);
hi_q = _mm256_and_si256(_mm256_srl_epi16(hi_q, shf1), shf1b);
hi_q = _mm256_add_epi8(mulhi, hi_q);
hi_q = _mm256_srl_epi16(hi_q, shf2); // 不进行符号扩展
// 将结果组合为一个 512 位向量,并与掩码进行与操作,扩展符号
return _mm512_and_si512(npyv512_combine_si256(lo_q, hi_q), shf2bw);
}
// divide each signed 8-bit element by divisor (round towards zero)
NPY_FINLINE npyv_s16 npyv_divc_s16(npyv_s16 a, const npyv_s16x3 divisor);
// 定义函数:将每个有符号8位元素除以除数(向零舍入)
NPY_FINLINE npyv_s8 npyv_divc_s8(npyv_s8 a, const npyv_s8x3 divisor)
{
// 计算偶数索引位置的结果
__m512i divc_even = npyv_divc_s16(npyv_shri_s16(npyv_shli_s16(a, 8), 8), divisor);
// 计算奇数索引位置的结果
__m512i divc_odd = npyv_divc_s16(npyv_shri_s16(a, 8), divisor);
// 对奇数索引位置的结果进行左移8位
divc_odd = npyv_shli_s16(divc_odd, 8);
// 使用掩码操作选择偶数索引位置的结果或者奇数索引位置的结果
return _mm512_mask_mov_epi8(divc_even, 0xAAAAAAAAAAAAAAAA, divc_odd);
// 定义位掩码以选择合适的结果
const __m512i bmask = _mm512_set1_epi32(0x00FF00FF);
// 使用位掩码选择偶数索引位置的结果或者奇数索引位置的结果
return npyv_select_u8(bmask, divc_even, divc_odd);
}
// divide each unsigned 16-bit element by divisor
// 定义函数:将每个无符号16位元素除以除数
NPY_FINLINE npyv_u16 npyv_divc_u16(npyv_u16 a, const npyv_u16x3 divisor)
{
// 将除数的第1和第2个元素转换为128位整数
const __m128i shf1 = _mm512_castsi512_si128(divisor.val[1]);
const __m128i shf2 = _mm512_castsi512_si128(divisor.val[2]);
// floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2
mulhi = _mm
R = _mm
R = _mm
R = _mm
R = _mm
// 使用AVX-512指令集进行无符号16位除法运算
__m512i mulhi, q;
NPYV__DIVC_U16(512, a, divisor.val[0], q)
return q;
// 获取除数的低256位部分
const __m256i m = npyv512_lower_si256(divisor.val[0]);
// 将输入向量的低256位和高256位分离
__m256i lo_a = npyv512_lower_si256(a);
__m256i hi_a = npyv512_higher_si256(a);
// 定义变量:乘积高位、低位结果向量
__m256i mulhi, lo_q, hi_q;
// 对低256位和高256位分别进行除法运算
NPYV__DIVC_U16(256, lo_a, m, lo_q)
NPYV__DIVC_U16(256, hi_a, m, hi_q)
// 将低位和高位的结果合并为一个512位向量并返回
return npyv512_combine_si256(lo_q, hi_q);
}
// divide each signed 16-bit element by divisor (round towards zero)
// 定义函数:将每个有符号16位元素除以除数(向零舍入)
NPY_FINLINE npyv_s16 npyv_divc_s16(npyv_s16 a, const npyv_s16x3 divisor)
{
// 将除数的第1个元素转换为128位整数
const __m128i shf1 = _mm512_castsi512_si128(divisor.val[1]);
// q = ((a + mulhi) >> sh1) - XSIGN(a)
// trunc(a/d) = (q ^ dsign) - dsign
mulhi = _mm
R = _mm
R = _mm
R = _mm
// 使用AVX-512指令集进行有符号16位除法运算
__m512i mulhi, q;
NPYV__DIVC_S16(512, a, divisor.val[0], divisor.val[2], q)
return q;
// 获取除数的低256位部分和符号掩码
const __m256i m = npyv512_lower_si256(divisor.val[0]);
const __m256i dsign = npyv512_lower_si256(divisor.val[2]);
// 将输入向量的低256位和高256位分离
__m256i lo_a = npyv512_lower_si256(a);
__m256i hi_a = npyv512_higher_si256(a);
// 定义变量:乘积高位、低位结果向量
__m256i mulhi, lo_q, hi_q;
// 对低256位和高256位分别进行除法运算
NPYV__DIVC_S16(256, lo_a, m, dsign, lo_q)
NPYV__DIVC_S16(256, hi_a, m, dsign, hi_q)
// 将低位和高位的结果合并为一个512位向量并返回
return npyv512_combine_si256(lo_q, hi_q);
}
// divide each unsigned 32-bit element by divisor
NPY_FINLINE npyv_u32 npyv_divc_u32(npyv_u32 a, const npyv_u32x3 divisor)
{
// 将除数的第一个部分加载为右移位数,用于高位无符号乘法
const __m128i shf1 = _mm512_castsi512_si128(divisor.val[1]);
// 将除数的第二个部分加载为右移位数,用于计算 floor(a/d) 的结果
const __m128i shf2 = _mm512_castsi512_si128(divisor.val[2]);
// 计算 a 和除数的第一个部分的无符号乘积的高位
__m512i mulhi_even = _mm512_srli_epi64(_mm512_mul_epu32(a, divisor.val[0]), 32);
// 计算 a 右移32位后与除数的第一个部分的无符号乘积的高位
__m512i mulhi_odd = _mm512_mul_epu32(_mm512_srli_epi64(a, 32), divisor.val[0]);
// 合并偶数和奇数位的高位乘积,使用掩码0xAAAA选择奇数位的结果
__m512i mulhi = _mm512_mask_mov_epi32(mulhi_even, 0xAAAA, mulhi_odd);
// 计算 floor(a/d) = (mulhi + ((a-mulhi) >> shf1)) >> shf2
__m512i q = _mm512_sub_epi32(a, mulhi);
q = _mm512_srl_epi32(q, shf1);
q = _mm512_add_epi32(mulhi, q);
q = _mm512_srl_epi32(q, shf2);
return q;
}
// divide each signed 32-bit element by divisor (round towards zero)
NPY_FINLINE npyv_s32 npyv_divc_s32(npyv_s32 a, const npyv_s32x3 divisor)
{
// 将除数的第一个部分加载为右移位数,用于有符号乘法的高位计算
const __m128i shf1 = _mm512_castsi512_si128(divisor.val[1]);
// 计算 a 和除数的第一个部分的有符号乘积的高位
__m512i mulhi_even = _mm512_srli_epi64(_mm512_mul_epi32(a, divisor.val[0]), 32);
// 计算 a 右移32位后与除数的第一个部分的有符号乘积的高位
__m512i mulhi_odd = _mm512_mul_epi32(_mm512_srli_epi64(a, 32), divisor.val[0]);
// 合并偶数和奇数位的高位乘积,使用掩码0xAAAA选择奇数位的结果
__m512i mulhi = _mm512_mask_mov_epi32(mulhi_even, 0xAAAA, mulhi_odd);
// 计算 trunc(a/d) = (q ^ dsign) - dsign,其中 q = ((a + mulhi) >> shf1) - XSIGN(a)
__m512i q = _mm512_sra_epi32(_mm512_add_epi32(a, mulhi), shf1);
q = _mm512_sub_epi32(q, _mm512_srai_epi32(a, 31));
q = _mm512_sub_epi32(_mm512_xor_si512(q, divisor.val[2]), divisor.val[2]);
return q;
}
// returns the high 64 bits of unsigned 64-bit multiplication
// xref https://stackoverflow.com/a/28827013
NPY_FINLINE npyv_u64 npyv__mullhi_u64(npyv_u64 a, npyv_u64 b)
{
// 创建用于低位掩码的常量向量
__m512i lomask = npyv_setall_s64(0xffffffff);
// 计算 a 和 b 的高32位部分
__m512i a_hi = _mm512_srli_epi64(a, 32); // a0l, a0h, a1l, a1h
__m512i b_hi = _mm512_srli_epi64(b, 32); // b0l, b0h, b1l, b1h
// 计算部分乘积
__m512i w0 = _mm512_mul_epu32(a, b); // a0l*b0l, a1l*b1l
__m512i w1 = _mm512_mul_epu32(a, b_hi); // a0l*b0h, a1l*b1h
__m512i w2 = _mm512_mul_epu32(a_hi, b); // a0h*b0l, a1h*b0l
__m512i w3 = _mm512_mul_epu32(a_hi, b_hi); // a0h*b0h, a1h*b1h
// 合并部分乘积
__m512i w0h = _mm512_srli_epi64(w0, 32);
__m512i s1 = _mm512_add_epi64(w1, w0h);
__m512i s1l = _mm512_and_si512(s1, lomask);
__m512i s1h = _mm512_srli_epi64(s1, 32);
__m512i s2 = _mm512_add_epi64(w2, s1l);
__m512i s2h = _mm512_srli_epi64(s2, 32);
__m512i hi = _mm512_add_epi64(w3, s1h);
hi = _mm512_add_epi64(hi, s2h);
return hi;
}
// divide each unsigned 64-bit element by a divisor
NPY_FINLINE npyv_u64 npyv_divc_u64(npyv_u64 a, const npyv_u64x3 divisor)
{
// 使用 SIMD 指令集将 divisor.val[1] 转换为 __m128i 类型
const __m128i shf1 = _mm512_castsi512_si128(divisor.val[1]);
// 使用 SIMD 指令集将 divisor.val[2] 转换为 __m128i 类型
const __m128i shf2 = _mm512_castsi512_si128(divisor.val[2]);
// 计算 a 和 divisor.val[0] 的无符号整数高位乘积
__m512i mulhi = npyv__mullhi_u64(a, divisor.val[0]);
// 计算 floor(a/d) 的近似值,其中 d = divisor.val[0]
// 使用 SIMD 指令集执行以下操作:
// 1. 计算 a - mulhi
// 2. 对结果右移 shf1 位
// 3. 将 mulhi 加到上述结果
// 4. 对结果右移 shf2 位
__m512i q = _mm512_sub_epi64(a, mulhi);
q = _mm512_srl_epi64(q, shf1);
q = _mm512_add_epi64(mulhi, q);
q = _mm512_srl_epi64(q, shf2);
// 返回计算得到的 floor(a/d) 的近似值
return q;
// divide each unsigned 64-bit element by a divisor (round towards zero)
NPY_FINLINE npyv_s64 npyv_divc_s64(npyv_s64 a, const npyv_s64x3 divisor)
{
// 将第二个128位部分作为移位掩码
const __m128i shf1 = _mm512_castsi512_si128(divisor.val[1]);
// 高位部分的无符号乘法结果
__m512i mulhi = npyv__mullhi_u64(a, divisor.val[0]);
// 将乘法结果转换为有符号高位乘法结果
// mulhi - ((a < 0) ? m : 0) - ((m < 0) ? a : 0);
__m512i asign = _mm512_srai_epi64(a, 63);
__m512i msign = _mm512_srai_epi64(divisor.val[0], 63);
__m512i m_asign = _mm512_and_si512(divisor.val[0], asign);
__m512i a_msign = _mm512_and_si512(a, msign);
mulhi = _mm512_sub_epi64(mulhi, m_asign);
mulhi = _mm512_sub_epi64(mulhi, a_msign);
// q = ((a + mulhi) >> sh1) - XSIGN(a)
// 截断(a/d) = (q ^ dsign) - dsign
__m512i q = _mm512_sra_epi64(_mm512_add_epi64(a, mulhi), shf1);
q = _mm512_sub_epi64(q, asign);
q = _mm512_sub_epi64(_mm512_xor_si512(q, divisor.val[2]), divisor.val[2]);
return q;
}
/***************************
* Division
***************************/
// TODO: emulate integer division
/***************************
* FUSED
***************************/
// multiply and add, a*b + c
// multiply and subtract, a*b - c
// negate multiply and add, -(a*b) + c
// negate multiply and subtract, -(a*b) - c
// multiply, add for odd elements and subtract even elements.
// (a * b) -+ c
/***************************
* Summation: Calculates the sum of all vector elements.
* there are three ways to implement reduce sum for AVX512:
* 1- split(256) /add /split(128) /add /hadd /hadd /extract
* 2- shuff(cross) /add /shuff(cross) /add /shuff /add /shuff /add /extract
* 3- _mm512_reduce_add_ps/pd
* The first one is been widely used by many projects
*
* the second one is used by Intel Compiler, maybe because the
* latency of hadd increased by (2-3) starting from Skylake-X which makes two
* extra shuffles(non-cross) cheaper. check https://godbolt.org/z/s3G9Er for more info.
*
* The third one is almost the same as the second one but only works for
* intel compiler/GCC 7.1/Clang 4, we still need to support older GCC.
***************************/
// reduce sum across vector
// 定义宏 npyv_sum_f64,用于计算 AVX-512 寄存器中双精度浮点数的累加和
// 定义一个内联函数,用于求解未定义情况下的无符号32位整数向量a的总和
NPY_FINLINE npy_uint32 npyv_sum_u32(npyv_u32 a)
{
// 将256位向量a分为低128位和高128位,并将它们相加
__m256i half = _mm256_add_epi32(npyv512_lower_si256(a), npyv512_higher_si256(a));
// 将得到的结果128位向量再进行一次加法操作,得到四个32位整数的和
__m128i quarter = _mm_add_epi32(_mm256_castsi256_si128(half), _mm256_extracti128_si256(half, 1));
// 将得到的128位向量进行水平加法操作,得到最终的32位整数和,并返回结果
quarter = _mm_hadd_epi32(quarter, quarter);
return _mm_cvtsi128_si32(_mm_hadd_epi32(quarter, quarter));
}
// 定义一个内联函数,用于求解未定义情况下的无符号64位整数向量a的总和
NPY_FINLINE npy_uint64 npyv_sum_u64(npyv_u64 a)
{
// 将256位向量a分为低128位和高128位,并将它们相加
__m256i four = _mm256_add_epi64(npyv512_lower_si256(a), npyv512_higher_si256(a));
// 对得到的256位向量进行一系列加法和位混洗操作,得到64位整数的总和
__m256i two = _mm256_add_epi64(four, _mm256_shuffle_epi32(four, _MM_SHUFFLE(1, 0, 3, 2)));
// 将得到的256位向量转换为128位,并进行一次128位的加法操作
__m128i one = _mm_add_epi64(_mm256_castsi256_si128(two), _mm256_extracti128_si256(two, 1));
// 返回结果向量中的64位整数总和
return (npy_uint64)npyv128_cvtsi128_si64(one);
}
// 定义一个内联函数,用于求解未定义情况下的单精度浮点数向量a的总和
NPY_FINLINE float npyv_sum_f32(npyv_f32 a)
{
// 对512位向量a进行一系列的加法和位混洗操作,得到最终的单精度浮点数总和
__m512 h64 = _mm512_shuffle_f32x4(a, a, _MM_SHUFFLE(3, 2, 3, 2));
__m512 sum32 = _mm512_add_ps(a, h64);
__m512 h32 = _mm512_shuffle_f32x4(sum32, sum32, _MM_SHUFFLE(1, 0, 3, 2));
__m512 sum16 = _mm512_add_ps(sum32, h32);
__m512 h16 = _mm512_permute_ps(sum16, _MM_SHUFFLE(1, 0, 3, 2));
__m512 sum8 = _mm512_add_ps(sum16, h16);
__m512 h4 = _mm512_permute_ps(sum8, _MM_SHUFFLE(2, 3, 0, 1));
__m512 sum4 = _mm512_add_ps(sum8, h4);
// 将最终的512位向量转换为128位向量,并返回其中的单精度浮点数总和
return _mm_cvtss_f32(_mm512_castps512_ps128(sum4));
}
// 定义一个内联函数,用于求解未定义情况下的双精度浮点数向量a的总和
NPY_FINLINE double npyv_sum_f64(npyv_f64 a)
{
// 对512位向量a进行一系列的加法和位混洗操作,得到最终的双精度浮点数总和
__m512d h64 = _mm512_shuffle_f64x2(a, a, _MM_SHUFFLE(3, 2, 3, 2));
__m512d sum32 = _mm512_add_pd(a, h64);
__m512d h32 = _mm512_permutex_pd(sum32, _MM_SHUFFLE(1, 0, 3, 2));
__m512d sum16 = _mm512_add_pd(sum32, h32);
__m512d h16 = _mm512_permute_pd(sum16, _MM_SHUFFLE(2, 3, 0, 1));
__m512d sum8 = _mm512_add_pd(sum16, h16);
// 将最终的512位向量转换为128位向量,并返回其中的双精度浮点数总和
return _mm_cvtsd_f64(_mm512_castpd512_pd128(sum8));
}
// 定义一个内联函数,用于对无符号8位整数向量a进行扩展并执行求和操作
// 当系统支持AVX512BW指令集时,使用512位向量进行求和
NPY_FINLINE npy_uint16 npyv_sumup_u8(npyv_u8 a)
{
// 对512位向量a执行无符号8位整数求和累计操作
__m512i eight = _mm512_sad_epu8(a, _mm512_setzero_si512());
// 将512位向量拆分为256位向量,并将其相加
__m256i four = _mm256_add_epi16(npyv512_lower_si256(eight), npyv512_higher_si256(eight));
// 当系统不支持AVX512BW指令集时,对低256位和高256位向量分别执行无符号8位整数求和累计操作,然后将其相加
__m256i lo_four = _mm256_sad_epu8(npyv512_lower_si256(a), _mm256_setzero_si256());
__m256i hi_four = _mm256_sad_epu8(npyv512_higher_si256(a), _mm256_setzero_si256());
__m256i four = _mm256_add_epi16(lo_four, hi_four);
// 将得到的256位向量再次拆分为128位向量,并进行一系列的加法操作,最终得到16位无符号整数的总和
__m128i two = _mm_add_epi16(_mm256_castsi256_si128(four), _mm256_extracti128_si256(four, 1));
__m128i one = _mm_add_epi16(two, _mm_unpackhi_epi64(two, two));
// 将最终的128位向量中的整数转换为16位无符号整数,并返回结果
return (npy_uint16)_mm_cvtsi128_si32(one);
}
// 定义一个内联函数,用于对无符号16位整数向量a执行扩展并执行求和操作
NPY_FINLINE npy_uint32 npyv_sumup_u16(npyv_u16 a)
{
// 创建一个掩码,用于提取偶数位的16位整数
const npyv_u16 even_mask = _mm512_set1_epi32(0x0000FFFF);
__m512i even = _mm512_and_si512(a, even_mask);
// 对原始向量右移16位,得到奇数位的16位整数,并将其相加
__m512i odd = _mm512_srli_epi32(a, 16);
__m512i ff = _mm512_add_epi32(even, odd);
// 调用npv_sum_u32函数,对上一步得到的32位整数向量求和,并返回结果
return npyv_sum_u32(ff);
}
.\numpy\numpy\_core\src\common\simd\avx512\avx512.h
// 如果未定义 _NPY_SIMD_H_,则输出错误信息,不是独立头文件
// 定义 NPY_SIMD 为 512,表示 SIMD 宽度为 512 比特
// 定义 NPY_SIMD_WIDTH 为 64,表示每个 SIMD 寄存器的宽度为 64 比特
// 定义 NPY_SIMD_F32 为 1,表示支持单精度浮点数的 SIMD 操作
// 定义 NPY_SIMD_F64 为 1,表示支持双精度浮点数的 SIMD 操作
// 定义 NPY_SIMD_FMA3 为 1,表示本机支持 Fused Multiply-Add (FMA3) 指令
// 定义 NPY_SIMD_BIGENDIAN 为 0,表示 SIMD 环境不是大端序
// 定义 NPY_SIMD_CMPSIGNAL 为 0,表示 SIMD 比较信号不支持
// 以下两个宏定义允许使用 _mm512_i32gather_* 和 _mm512_i32scatter_*,限制为内存加载和存储的最大步长
typedef __m512i npyv_u8;
typedef __m512i npyv_s8;
typedef __m512i npyv_u16;
typedef __m512i npyv_s16;
typedef __m512i npyv_u32;
typedef __m512i npyv_s32;
typedef __m512i npyv_u64;
typedef __m512i npyv_s64;
typedef __m512 npyv_f32;
typedef __m512d npyv_f64;
// 如果定义了 NPY_HAVE_AVX512BW,使用 AVX-512BW 指令集
typedef __mmask64 npyv_b8;
typedef __mmask32 npyv_b16;
// 如果未定义 NPY_HAVE_AVX512BW,使用普通的 AVX-512 整型寄存器
typedef __m512i npyv_b8;
typedef __m512i npyv_b16;
// 定义 AVX-512 下的掩码类型,根据 NPY_HAVE_AVX512BW 的定义选择 __mmask* 或 __m512i
typedef __mmask16 npyv_b32;
typedef __mmask8 npyv_b64;
typedef struct { __m512i val[2]; } npyv_m512ix2;
typedef npyv_m512ix2 npyv_u8x2;
typedef npyv_m512ix2 npyv_s8x2;
typedef npyv_m512ix2 npyv_u16x2;
typedef npyv_m512ix2 npyv_s16x2;
typedef npyv_m512ix2 npyv_u32x2;
typedef npyv_m512ix2 npyv_s32x2;
typedef npyv_m512ix2 npyv_u64x2;
typedef npyv_m512ix2 npyv_s64x2;
typedef struct { __m512i val[3]; } npyv_m512ix3;
typedef npyv_m512ix3 npyv_u8x3;
typedef npyv_m512ix3 npyv_s8x3;
typedef npyv_m512ix3 npyv_u16x3;
typedef npyv_m512ix3 npyv_s16x3;
typedef npyv_m512ix3 npyv_u32x3;
typedef npyv_m512ix3 npyv_s32x3;
typedef npyv_m512ix3 npyv_u64x3;
typedef npyv_m512ix3 npyv_s64x3;
typedef struct { __m512 val[2]; } npyv_f32x2;
typedef struct { __m512d val[2]; } npyv_f64x2;
typedef struct { __m512 val[3]; } npyv_f32x3;
typedef struct { __m512d val[3]; } npyv_f64x3;
.\numpy\numpy\_core\src\common\simd\avx512\conversion.h
// 如果未定义NPY_SIMD,抛出错误信息,要求不要单独使用此头文件
// 如果支持AVX512BW,则使用AVX512指令集中的_mm512_movm_epi8函数进行无符号8位整数向量到掩码的转换
// 如果支持AVX512BW,则使用AVX512指令集中的_mm512_movm_epi16函数进行无符号16位整数向量到掩码的转换
// 否则,定义宏npv_cvt_u8_b8和npyv_cvt_u16_b16分别为输入和输出相等
// npyv_cvt_s8_b8和npyv_cvt_s16_b16分别为npv_cvt_u8_b8和npyv_cvt_u16_b16的别名
// 如果支持AVX512DQ,则使用AVX512指令集中的_mm512_movm_epi32函数进行无符号32位整数向量到掩码的转换
// 如果支持AVX512DQ,则使用AVX512指令集中的_mm512_movm_epi64函数进行无符号64位整数向量到掩码的转换
// 否则,定义宏npyv_cvt_u32_b32和npyv_cvt_u64_b64为使用BL和指定值-1作为输入
// npyv_cvt_s32_b32和npyv_cvt_s64_b64分别为npyv_cvt_u32_b32和npyv_cvt_u64_b64的别名
// 定义npv_cvt_f32_b32和npv_cvt_f64_b64分别为_mm512_castsi512_ps和npyv_cvt_u32_b32和npyv_cvt_u64_b64的别名
// 将整数向量转换为掩码
// 如果支持AVX512BW,则使用AVX512指令集中的_mm512_movepi8_mask和_mm512_movepi16_mask分别进行8位和16位整数向量到掩码的转换
// 否则,定义宏npyv_cvt_b8_u8和npyv_cvt_b16_u16为输入
// npyv_cvt_b8_s8和npyv_cvt_b16_s16分别为npyv_cvt_b8_u8和npyv_cvt_b16_u16的别名
// 如果支持AVX512DQ,则使用AVX512指令集中的_mm512_movepi32_mask和_mm512_movepi64_mask分别进行32位和64位整数向量到掩码的转换
// 否则,定义宏npyv_cvt_b32_u32和npyv_cvt_b64_u64为使用A和_mm512_setzero_si512作为输入
// npyv_cvt_b32_s32和npyv_cvt_b64_s64分别为npyv_cvt_b32_u32和npyv_cvt_b64_u64的别名
// 定义npv_cvt_b32_f32和npv_cvt_b64_f64分别为npyv_cvt_b32_u32和npyv_cvt_b64_u64的别名
// 扩展函数
NPY_FINLINE npyv_u16x2 npyv_expand_u16_u8(npyv_u8 data)
{
npyv_u16x2 r;
// 获取数据的低256位和高256位
__m256i lo = npyv512_lower_si256(data);
__m256i hi = npyv512_higher_si256(data);
// 如果支持AVX512BW,则将低256位和高256位的无符号8位整数转换为无符号16位整数,分别存入r的第一个和第二个元素
r.val[0] = _mm512_cvtepu8_epi16(lo);
r.val[1] = _mm512_cvtepu8_epi16(hi);
// 否则,将低256位和高256位的无符号8位整数先分别转换为无符号16位整数,然后组合成256位,分别存入r的第一个和第二个元素
__m256i loelo = _mm256_cvtepu8_epi16(_mm256_castsi256_si128(lo));
__m256i loehi = _mm256_cvtepu8_epi16(_mm256_extracti128_si256(lo, 1));
__m256i hielo = _mm256_cvtepu8_epi16(_mm256_castsi256_si128(hi));
__m256i hiehi = _mm256_cvtepu8_epi16(_mm256_extracti128_si256(hi, 1));
r.val[0] = npyv512_combine_si256(loelo, loehi);
r.val[1] = npyv512_combine_si256(hielo, hiehi);
return r;
}
NPY_FINLINE npyv_u32x2 npyv_expand_u32_u16(npyv_u16 data)
{
npyv_u32x2 r;
// 获取数据的低256位和高256位
__m256i lo = npyv512_lower_si256(data);
__m256i hi = npyv512_higher_si256(data);
// 如果支持AVX512BW,则将低256位和高256位的
__m256i hielo = _mm256_cvtepu16_epi32(_mm256_castsi256_si128(hi));
__m256i hiehi = _mm256_cvtepu16_epi32(_mm256_extracti128_si256(hi, 1));
r.val[0] = npyv512_combine_si256(loelo, loehi);
r.val[1] = npyv512_combine_si256(hielo, hiehi);
return r;
}
// 将两个16位布尔值打包成一个8位布尔向量
NPY_FINLINE npyv_b8 npyv_pack_b8_b16(npyv_b16 a, npyv_b16 b) {
// 使用AVX-512指令集进行位解压缩和打包
return _mm512_kunpackd((__mmask64)b, (__mmask64)a);
// 创建索引以重新排列16位整数元素
const __m512i idx = _mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7);
// 使用AVX-512指令集将两个16位整数向量打包成8位整数向量
return _mm512_permutexvar_epi64(idx, npyv512_packs_epi16(a, b));
}
// 将四个32位布尔向量打包成一个8位布尔向量
NPY_FINLINE npyv_b8
npyv_pack_b8_b32(npyv_b32 a, npyv_b32 b, npyv_b32 c, npyv_b32 d) {
// 使用AVX-512指令集进行位解压缩和打包
__mmask32 ab = _mm512_kunpackw((__mmask32)b, (__mmask32)a);
__mmask32 cd = _mm512_kunpackw((__mmask32)d, (__mmask32)c);
// 调用上一函数,将两个32位布尔向量打包成一个8位布尔向量
return npyv_pack_b8_b16(ab, cd);
// 创建索引以重新排列32位整数元素
const __m512i idx = _mm512_setr_epi32(
0, 4, 1, 5, 2, 6, 3, 7, 8, 12, 9, 13, 10, 14, 11, 15);
// 将四个32位整数向量打包成两个16位整数向量
__m256i ta = npyv512_pack_lo_hi(npyv_cvt_u32_b32(a));
__m256i tb = npyv512_pack_lo_hi(npyv_cvt_u32_b32(b));
__m256i tc = npyv512_pack_lo_hi(npyv_cvt_u32_b32(c));
__m256i td = npyv512_pack_lo_hi(npyv_cvt_u32_b32(d));
// 将两个16位整数向量打包成8位整数向量
__m256i ab = _mm256_packs_epi16(ta, tb);
__m256i cd = _mm256_packs_epi16(tc, td);
// 将两个8位整数向量打包成一个8位整数向量
__m512i abcd = npyv512_combine_si256(ab, cd);
// 使用AVX-512指令集根据索引重新排列元素
return _mm512_permutexvar_epi32(idx, abcd);
}
// 将八个64位布尔向量打包成一个8位布尔向量
NPY_FINLINE npyv_b8
npyv_pack_b8_b64(npyv_b64 a, npyv_b64 b, npyv_b64 c, npyv_b64 d,
npyv_b64 e, npyv_b64 f, npyv_b64 g, npyv_b64 h) {
// 使用AVX-512指令集进行位解压缩和打包
__mmask16 ab = _mm512_kunpackb((__mmask16)b, (__mmask16)a);
__mmask16 cd = _mm512_kunpackb((__mmask16)d, (__mmask16)c);
__mmask16 ef = _mm512_kunpackb((__mmask16)f, (__mmask16)e);
__mmask16 gh = _mm512_kunpackb((__mmask16)h, (__mmask16)g);
// 调用上一函数,将四个16位布尔向量打包成一个8位布尔向量
return npyv_pack_b8_b32(ab, cd, ef, gh);
}
/*
* A compiler bug workaround on Intel Compiler Classic.
* The bug manifests specifically when the
* scalar result of _cvtmask64_u64 is compared against the constant -1. This
* comparison uniquely triggers a bug under conditions of equality (==) or
* inequality (!=) checks, which are typically used in reduction operations like
* np.logical_or.
*
* The underlying issue arises from the compiler's optimizer. When the last
* vector comparison instruction operates on zmm, the optimizer erroneously
* emits a duplicate of this instruction but on the lower half register ymm. It
* then performs a bitwise XOR operation between the mask produced by this
* duplicated instruction and the mask from the original comparison instruction.
* This erroneous behavior leads to incorrect results.
*
* See https://github.com/numpy/numpy/issues/26197#issuecomment-2056750975
*/
#ifdef __INTEL_COMPILER
// 使用volatile修饰符以解决Intel编译器经典版本上的编译器错误
#define NPYV__VOLATILE_CVTMASK64 volatile
#else
// 在非Intel编译器上不使用volatile修饰符
#define NPYV__VOLATILE_CVTMASK64
#endif
// 将布尔向量转换为整数位域
NPY_FINLINE npy_uint64 npyv_tobits_b8(npyv_b8 a) {
#ifdef NPY_HAVE_AVX512BW_MASK
// 将布尔向量转换为64位整数
npy_uint64 NPYV__VOLATILE_CVTMASK64 t = (npy_uint64)_cvtmask64_u64(a);
return t;
#elif defined(NPY_HAVE_AVX512BW)
# 如果定义了 NPY_HAVE_AVX512BW,则执行以下代码段
npy_uint64 NPYV__VOLATILE_CVTMASK64 t = (npy_uint64)a;
return t;
#else
# 否则执行以下代码段
int mask_lo = _mm256_movemask_epi8(npyv512_lower_si256(a));
int mask_hi = _mm256_movemask_epi8(npyv512_higher_si256(a));
return (unsigned)mask_lo | ((npy_uint64)(unsigned)mask_hi << 32);
#endif
}
#undef NPYV__VOLATILE_CVTMASK64
NPY_FINLINE npy_uint64 npyv_tobits_b16(npyv_b16 a)
{
#ifdef NPY_HAVE_AVX512BW_MASK
# 如果定义了 NPY_HAVE_AVX512BW_MASK,则执行以下代码段
return (npy_uint32)_cvtmask32_u32(a);
#elif defined(NPY_HAVE_AVX512BW)
# 如果定义了 NPY_HAVE_AVX512BW,则执行以下代码段
return (npy_uint32)a;
#else
# 否则执行以下代码段
__m256i pack = _mm256_packs_epi16(
npyv512_lower_si256(a), npyv512_higher_si256(a)
);
return (npy_uint32)_mm256_movemask_epi8(_mm256_permute4x64_epi64(pack, _MM_SHUFFLE(3, 1, 2, 0)));
#endif
}
NPY_FINLINE npy_uint64 npyv_tobits_b32(npyv_b32 a)
{ return (npy_uint16)a; }
NPY_FINLINE npy_uint64 npyv_tobits_b64(npyv_b64 a)
{
#ifdef NPY_HAVE_AVX512DQ_MASK
# 如果定义了 NPY_HAVE_AVX512DQ_MASK,则执行以下代码段
return _cvtmask8_u32(a);
#else
# 否则执行以下代码段
return (npy_uint8)a;
#endif
}
// round to nearest integer (assuming even)
#define npyv_round_s32_f32 _mm512_cvtps_epi32
NPY_FINLINE npyv_s32 npyv_round_s32_f64(npyv_f64 a, npyv_f64 b)
{
// 将 a 和 b 转换为整数(向最近的偶数舍入)
__m256i lo = _mm512_cvtpd_epi32(a), hi = _mm512_cvtpd_epi32(b);
// 将两个 __m256i 类型的变量合并成一个 npyv_s32 类型的变量并返回
return npyv512_combine_si256(lo, hi);
}
#endif // _NPY_SIMD_AVX512_CVT_H
.\numpy\numpy\_core\src\common\simd\avx512\maskop.h
/**
* Implements conditional addition and subtraction.
* e.g. npyv_ifadd_f32(m, a, b, c) -> m ? a + b : c
* e.g. npyv_ifsub_f32(m, a, b, c) -> m ? a - b : c
*/
// 定义 AVX512 下的条件加法和减法操作
NPY_FINLINE npyv_
(npyv_
{ \
// 执行向量加法 a + b
npyv_
// 根据掩码 m 选择返回加法结果 add 或者 c
return npyv_select_
} \
NPY_FINLINE npyv_
(npyv_
{ \
// 执行向量减法 a - b
npyv_
// 根据掩码 m 选择返回减法结果 sub 或者 c
return npyv_select_
}
// 定义 AVX512 下的条件加法和减法操作,使用 AVX512 指令
NPY_FINLINE npyv_
(npyv_
{ return _mm512_mask_add_
NPY_FINLINE npyv_
(npyv_
{ return _mm512_mask_sub_
// 如果支持 AVX512BW,则使用 AVX512 指令实现 u8, s8, u16, s16 类型的条件加减法
NPYV_IMPL_AVX512_MASK_ADDSUB(u8, b8, epi8)
NPYV_IMPL_AVX512_MASK_ADDSUB(s8, b8, epi8)
NPYV_IMPL_AVX512_MASK_ADDSUB(u16, b16, epi16)
NPYV_IMPL_AVX512_MASK_ADDSUB(s16, b16, epi16)
// 如果不支持 AVX512BW,则使用仿真方式实现 u8, s8, u16, s16 类型的条件加减法
NPYV_IMPL_AVX512_EMULATE_MASK_ADDSUB(u8, b8)
NPYV_IMPL_AVX512_EMULATE_MASK_ADDSUB(s8, b8)
NPYV_IMPL_AVX512_EMULATE_MASK_ADDSUB(u16, b16)
NPYV_IMPL_AVX512_EMULATE_MASK_ADDSUB(s16, b16)
// 使用 AVX512 指令实现 u32, s32, u64, s64, f32, f64 类型的条件加减法
NPYV_IMPL_AVX512_MASK_ADDSUB(u32, b32, epi32)
NPYV_IMPL_AVX512_MASK_ADDSUB(s32, b32, epi32)
NPYV_IMPL_AVX512_MASK_ADDSUB(u64, b64, epi64)
NPYV_IMPL_AVX512_MASK_ADDSUB(s64, b64, epi64)
NPYV_IMPL_AVX512_MASK_ADDSUB(f32, b32, ps)
NPYV_IMPL_AVX512_MASK_ADDSUB(f64, b64, pd)
// 使用 AVX512 指令实现条件除法,m ? a / b : c
NPY_FINLINE npyv_f32 npyv_ifdiv_f32(npyv_b32 m, npyv_f32 a, npyv_f32 b, npyv_f32 c)
{ return _mm512_mask_div_ps(c, m, a, b); }
// 使用 AVX512 指令实现条件除法,m ? a / b : 0
NPY_FINLINE npyv_f32 npyv_ifdivz_f32(npyv_b32 m, npyv_f32 a, npyv_f32 b)
{ return _mm512_maskz_div_ps(m, a, b); }
// 使用 AVX512 指令实现条件除法,m ? a / b : c
NPY_FINLINE npyv_f64 npyv_ifdiv_f64(npyv_b32 m, npyv_f64 a, npyv_f64 b, npyv_f64 c)
{ return _mm512_mask_div_pd(c, m, a, b); }
// 使用 AVX512 指令实现条件除法,m ? a / b : 0
NPY_FINLINE npyv_f64 npyv_ifdivz_f64(npyv_b32 m, npyv_f64 a, npyv_f64 b)
{ return _mm512_maskz_div_pd(m, a, b); }
.\numpy\numpy\_core\src\common\simd\avx512\math.h
/***************************
* Elementary
***************************/
// Square root functions for AVX512, operating on vectors of single and double precision floating point numbers
// Reciprocal functions for AVX512, computing the reciprocal of each element in a vector
NPY_FINLINE npyv_f32 npyv_recip_f32(npyv_f32 a)
{ return _mm512_div_ps(_mm512_set1_ps(1.0f), a); }
NPY_FINLINE npyv_f64 npyv_recip_f64(npyv_f64 a)
{ return _mm512_div_pd(_mm512_set1_pd(1.0), a); }
// Absolute value functions for AVX512, computing absolute values of vectors of single and double precision floating point numbers
NPY_FINLINE npyv_f32 npyv_abs_f32(npyv_f32 a)
{
return _mm512_range_ps(a, a, 8);
return npyv_and_f32(
a, _mm512_castsi512_ps(_mm512_set1_epi32(0x7fffffff))
);
}
NPY_FINLINE npyv_f64 npyv_abs_f64(npyv_f64 a)
{
return _mm512_range_pd(a, a, 8);
return npyv_and_f64(
a, _mm512_castsi512_pd(npyv_setall_s64(0x7fffffffffffffffLL))
);
}
// Square functions for AVX512, computing element-wise squares of vectors of single and double precision floating point numbers
NPY_FINLINE npyv_f32 npyv_square_f32(npyv_f32 a)
{ return _mm512_mul_ps(a, a); }
NPY_FINLINE npyv_f64 npyv_square_f64(npyv_f64 a)
{ return _mm512_mul_pd(a, a); }
// Maximum functions for AVX512, computing element-wise maximums of vectors of single and double precision floating point numbers
// Maximum with propagation of NaNs for single precision floating point numbers in AVX512
NPY_FINLINE npyv_f32 npyv_maxp_f32(npyv_f32 a, npyv_f32 b)
{
__mmask16 nn = _mm512_cmp_ps_mask(b, b, _CMP_ORD_Q);
return _mm512_mask_max_ps(a, nn, a, b);
}
// Maximum with propagation of NaNs for double precision floating point numbers in AVX512
NPY_FINLINE npyv_f64 npyv_maxp_f64(npyv_f64 a, npyv_f64 b)
{
__mmask8 nn = _mm512_cmp_pd_mask(b, b, _CMP_ORD_Q);
return _mm512_mask_max_pd(a, nn, a, b);
}
// Maximum with NaN handling for single precision floating point numbers in AVX512
NPY_FINLINE npyv_f32 npyv_maxn_f32(npyv_f32 a, npyv_f32 b)
{
__mmask16 nn = _mm512_cmp_ps_mask(a, a, _CMP_ORD_Q);
return _mm512_mask_max_ps(a, nn, a, b);
}
// Maximum with NaN handling for double precision floating point numbers in AVX512
NPY_FINLINE npyv_f64 npyv_maxn_f64(npyv_f64 a, npyv_f64 b)
{
__mmask8 nn = _mm512_cmp_pd_mask(a, a, _CMP_ORD_Q);
return _mm512_mask_max_pd(a, nn, a, b);
}
// Maximum functions for integer types in AVX512, using unsigned and signed 8, 16, 32, and 64-bit integers
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_max_u8, _mm256_max_epu8)
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_max_s8, _mm256_max_epi8)
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_max_u16, _mm256_max_epu16)
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_max_s16, _mm256_max_epi16)
// Minimum functions for AVX512, computing element-wise minimums of vectors of single precision floating point numbers
// 定义宏,将 _mm512_min_pd 重命名为 npyv_min_f64,用于执行双精度浮点数的最小值操作
// 返回 a 和 b 中每个对应元素的最小值,支持 IEEE 浮点算术(IEC 60559)
// - 如果其中一个向量包含 NaN,则设置另一个向量对应元素的值
// - 只有当两个对应元素都为 NaN 时,才设置 NaN
NPY_FINLINE npyv_f32 npyv_minp_f32(npyv_f32 a, npyv_f32 b)
{
__mmask16 nn = _mm512_cmp_ps_mask(b, b, _CMP_ORD_Q);
// 使用掩码 nn,将 a 和 b 中不是 NaN 的元素对应位置的最小值组成新向量返回
return _mm512_mask_min_ps(a, nn, a, b);
}
// 返回 a 和 b 中每个对应元素的最小值,支持 IEEE 双精度浮点数算术(IEC 60559)
// - 如果其中一个向量包含 NaN,则设置另一个向量对应元素的值
// - 只有当两个对应元素都为 NaN 时,才设置 NaN
NPY_FINLINE npyv_f64 npyv_minp_f64(npyv_f64 a, npyv_f64 b)
{
__mmask8 nn = _mm512_cmp_pd_mask(b, b, _CMP_ORD_Q);
// 使用掩码 nn,将 a 和 b 中不是 NaN 的元素对应位置的最小值组成新向量返回
return _mm512_mask_min_pd(a, nn, a, b);
}
// 返回 a 和 b 中每个对应元素的最小值,支持 IEEE 浮点算术(IEC 60559),传播 NaN
// - 如果任何对应元素为 NaN,则将该位置设置为 NaN
NPY_FINLINE npyv_f32 npyv_minn_f32(npyv_f32 a, npyv_f32 b)
{
__mmask16 nn = _mm512_cmp_ps_mask(a, a, _CMP_ORD_Q);
// 使用掩码 nn,将 a 和 b 中 a 不是 NaN 的元素对应位置的最小值组成新向量返回
return _mm512_mask_min_ps(a, nn, a, b);
}
// 返回 a 和 b 中每个对应元素的最小值,支持 IEEE 双精度浮点数算术(IEC 60559),传播 NaN
// - 如果任何对应元素为 NaN,则将该位置设置为 NaN
NPY_FINLINE npyv_f64 npyv_minn_f64(npyv_f64 a, npyv_f64 b)
{
__mmask8 nn = _mm512_cmp_pd_mask(a, a, _CMP_ORD_Q);
// 使用掩码 nn,将 a 和 b 中 a 不是 NaN 的元素对应位置的最小值组成新向量返回
return _mm512_mask_min_pd(a, nn, a, b);
}
// 返回 a 和 b 中每个对应元素的最小值,支持整数操作
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_min_u8, _mm256_min_epu8)
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_min_s8, _mm256_min_epi8)
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_min_u16, _mm256_min_epu16)
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_min_s16, _mm256_min_epi16)
// 32位和64位的减少最小和最大值的实现
// 定义 AVX-512 下的最小值和最大值归约函数,参数分别为数据类型、指令类型、向量指令类型
NPY_FINLINE STYPE
{ \
// 将512位整型寄存器拆分为两个256位整型寄存器
__m256i v256 = _mm256_
npyv512_higher_si256(a)); \
// 从256位整型寄存器中抽取低128位整型寄存器
__m128i v128 = _mm_
_mm256_extracti128_si256(v256, 1)); \
// 使用 Shuffle 指令对128位整型寄存器进行重排,得到64位整型寄存器
__m128i v64 = _mm_
(_MM_PERM_ENUM)_MM_SHUFFLE(0, 0, 3, 2))); \
// 再次使用 Shuffle 指令对64位整型寄存器进行重排,得到32位整型寄存器
__m128i v32 = _mm_
(_MM_PERM_ENUM)_MM_SHUFFLE(0, 0, 0, 1))); \
// 将最终结果转换为32位整数并返回
return (STYPE
} \
// 定义 AVX-512 下的最小值和最大值归约函数,参数分别为数据类型、指令类型、向量指令类型
NPY_FINLINE STYPE
{ \
// 使用 Shuffle 指令对512位整型寄存器进行重排,得到256位整型寄存器
__m512i v256 = _mm512_
_mm512_shuffle_i64x2(a, a, (_MM_PERM_ENUM)_MM_SHUFFLE(0, 0, 3, 2))); \
// 再次使用 Shuffle 指令对256位整型寄存器进行重排,得到128位整型寄存器
__m512i v128 = _mm512_
_mm512_shuffle_i64x2(v256, v256, (_MM_PERM_ENUM)_MM_SHUFFLE(0, 0, 0, 1))); \
// 使用 Shuffle 指令对128位整型寄存器进行重排,得到64位整型寄存器
__m512i v64 = _mm512_
_mm512_shuffle_epi32(v128, (_MM_PERM_ENUM)_MM_SHUFFLE(0, 0, 3, 2))); \
// 提取64位整型寄存器中的第一个元素,并将结果转换为64位整数返回
return (STYPE
}
// 定义 AVX-512 下的最小值和最大值归约函数的具体实现
NPY_IMPL_AVX512_REDUCE_MINMAX(npy_uint, min_u, min_epu)
NPY_IMPL_AVX512_REDUCE_MINMAX(npy_int, min_s, min_epi)
NPY_IMPL_AVX512_REDUCE_MINMAX(npy_uint, max_u, max_epu)
NPY_IMPL_AVX512_REDUCE_MINMAX(npy_int, max_s, max_epi)
// 取消宏定义 NPY_IMPL_AVX512_REDUCE_MINMAX,并注释说明为 ps & pd 的最小值和最大值归约
// 定义 AVX512 下的最小值和最大值归约函数,使用指定的 INTRIN 操作
NPY_FINLINE float npyv_reduce_
{ \
// 使用 AVX2 指令集下的 _mm256_
__m256 v256 = _mm256_
npyv512_lower_ps256(a), npyv512_higher_ps256(a)); \
// 将结果从 AVX256 转换为 AVX128,继续使用 AVX 指令进行归约
__m128 v128 = _mm_
_mm256_castps256_ps128(v256), _mm256_extractf128_ps(v256, 1)); \
// 在 AVX128 中进行进一步的归约操作
__m128 v64 = _mm_
_mm_shuffle_ps(v128, v128, (_MM_PERM_ENUM)_MM_SHUFFLE(0, 0, 3, 2))); \
// 继续使用 AVX 指令在更低精度下进行最后的归约操作
__m128 v32 = _mm_
_mm_shuffle_ps(v64, v64, (_MM_PERM_ENUM)_MM_SHUFFLE(0, 0, 0, 1))); \
// 返回最终结果,将 AVX32 结果转换为 float 类型
return _mm_cvtss_f32(v32); \
} \
// 定义 AVX512 下的双精度最小值和最大值归约函数,使用指定的 INTRIN 操作
NPY_FINLINE double npyv_reduce_
{ \
// 使用 AVX2 指令集下的 _mm256_
__m256d v256 = _mm256_
npyv512_lower_pd256(a), npyv512_higher_pd256(a)); \
// 将结果从 AVX256 转换为 AVX128,继续使用 AVX 指令进行归约
__m128d v128 = _mm_
_mm256_castpd256_pd128(v256), _mm256_extractf128_pd(v256, 1)); \
// 在 AVX128 中进行进一步的归约操作
__m128d v64 = _mm_
_mm_shuffle_pd(v128, v128, (_MM_PERM_ENUM)_MM_SHUFFLE(0, 0, 0, 1))); \
// 返回最终结果,将 AVX64 结果转换为 double 类型
return _mm_cvtsd_f64(v64); \
}
// 取消定义 NPY_IMPL_AVX512_REDUCE_MINMAX 宏
定义宏 NPY_IMPL_AVX512_REDUCE_MINMAX,带有三个参数:INTRIN、INF、INF64。
NPY_FINLINE float npyv_reduce_
定义内联函数 npyv_reduce_
{
函数体开始。
npyv_b32 notnan = npyv_notnan_f32(a);
声明并初始化变量 notnan,用于存储对 a 应用 npyv_notnan_f32 函数的结果。
if (NPY_UNLIKELY(!npyv_any_b32(notnan))) {
如果 notnan 中不存在任何 true 值(即没有非 NaN 的元素),执行以下代码块。
return _mm_cvtss_f32(_mm512_castps512_ps128(a));
将 a 强制转换为 128 位宽度的单精度浮点数,并返回其值。
}
条件语句结束。
a = npyv_select_f32(notnan, a,
npyv_reinterpret_f32_u32(npyv_setall_u32(INF)));
通过 npyv_select_f32 函数,根据 notnan 的条件选择将 a 中的元素保留或替换为 INF 的值。
return npyv_reduce_
调用 npyv_reduce_
}
函数体结束。
依此类推,对于每一个函数,需要按照类似的方式注释解释每一行代码的作用,确保完整理解每个函数的实现和功能。
// 定义 AVX512 指令的宏,用于实现最小值和最大值的归约操作
// 定义内联函数,对 __m512i 类型的数据进行最小值或最大值的归约操作,返回 STYPE
NPY_FINLINE STYPE
{ \
// 将 __m512i 类型数据的低 256 位和高 256 位分别提取为 __m256i 类型
__m256i v256 = _mm256_
// 从 __m256i 中提取低 128 位和高 128 位,执行相同的指令操作
__m128i v128 = _mm_
// 将低 128 位的数据向高位扩展
__m128i v64 = _mm_
(_MM_PERM_ENUM)_MM_SHUFFLE(0, 0, 3, 2))); \
// 继续向高位扩展为 32 位
__m128i v32 = _mm_
(_MM_PERM_ENUM)_MM_SHUFFLE(0, 0, 0, 1))); \
// 再次向高位扩展为 16 位
__m128i v16 = _mm_
(_MM_PERM_ENUM)_MM_SHUFFLE(0, 0, 0, 1))); \
// 将 16 位转换为 32 位,并返回最终结果
return (STYPE
} \
// 定义内联函数,对 __m512i 类型的数据进行最小值或最大值的归约操作,返回 STYPE
NPY_FINLINE STYPE
{ \
// 将 __m512i 类型数据的低 256 位和高 256 位分别提取为 __m256i 类型
__m256i v256 = _mm256_
// 从 __m256i 中提取低 128 位和高 128 位,执行相同的指令操作
__m128i v128 = _mm_
// 将低 128 位的数据向高位扩展
__m128i v64 = _mm_
(_MM_PERM_ENUM)_MM_SHUFFLE(0, 0, 3, 2))); \
// 继续向高位扩展为 32 位
__m128i v32 = _mm_
(_MM_PERM_ENUM)_MM_SHUFFLE(0, 0, 0, 1))); \
// 再次向高位扩展为 16 位
__m128i v16 = _mm_
(_MM_PERM_ENUM)_MM_SHUFFLE(0, 0, 0, 1))); \
// 将 16 位向高位扩展为 8 位,并返回最终结果
__m128i v8 = _mm_
return (STYPE
}
// 定义 AVX512 指令集下的最小最大值归约操作,针对无符号整数,使用最小值操作宏
NPY_IMPL_AVX512_REDUCE_MINMAX(npy_uint, min_u, min_epu)
// 定义 AVX512 指令集下的最小最大值归约操作,针对有符号整数,使用最小值操作宏
NPY_IMPL_AVX512_REDUCE_MINMAX(npy_int, min_s, min_epi)
// 定义 AVX512 指令集下的最小最大值归约操作,针对无符号整数,使用最大值操作宏
NPY_IMPL_AVX512_REDUCE_MINMAX(npy_uint, max_u, max_epu)
// 定义 AVX512 指令集下的最小最大值归约操作,针对有符号整数,使用最大值操作宏
NPY_IMPL_AVX512_REDUCE_MINMAX(npy_int, max_s, max_epi)
// 取消前面定义的 AVX512 最小最大值归约操作宏
// 定义向最近偶数整数舍入的 AVX512 单精度浮点数运算宏
// 定义向最近偶数整数舍入的 AVX512 双精度浮点数运算宏
// 定义向正无穷大舍入的 AVX512 单精度浮点数运算宏
// 定义向正无穷大舍入的 AVX512 双精度浮点数运算宏
// 定义向零舍入的 AVX512 单精度浮点数运算宏
// 定义向零舍入的 AVX512 双精度浮点数运算宏
// 定义向负无穷大舍入的 AVX512 单精度浮点数运算宏
// 定义向负无穷大舍入的 AVX512 双精度浮点数运算宏
// 结束 _NPY_SIMD_AVX512_MATH_H 文件的条件编译
.\numpy\numpy\_core\src\common\simd\avx512\memory.h
// 如果没有定义 NPY_SIMD 宏,则输出错误信息 "Not a standalone header"
/***************************
* load/store
***************************/
// 如果使用 GCC 编译器,因为 GCC 期望指针参数类型为 `void*` 而不是 `const void*`,会引发大量警告,
// 所以使用 `_mm512_stream_load_si512` 进行加载操作
// 对于其他编译器,正常使用 `_mm512_stream_load_si512` 加载操作
// 解决 MSVC(32位) 的溢出 bug,详见 https://developercommunity.visualstudio.com/content/problem/911872/u.html
NPY_FINLINE __m512i npyv__loadl(const __m256i *ptr)
{
// 使用 `_mm256_loadu_si256` 加载 `ptr` 所指向的内存,并将结果插入到 `_mm512_castsi256_si512` 的结果中返回
__m256i a = _mm256_loadu_si256(ptr);
return _mm512_inserti64x4(_mm512_castsi256_si512(a), a, 0);
}
// 对于其他情况,使用 `_mm256_loadu_si256` 加载 `PTR` 所指向的内存,然后使用 `_mm512_castsi256_si512` 转换返回结果
_mm512_castsi256_si512(_mm256_loadu_si256(PTR))
// 定义 AVX-512 内存操作的宏实现,包括不同类型的加载和存储操作
NPY_FINLINE npyv_
{ return _mm512_loadu_si512((const __m512i*)ptr); } \
NPY_FINLINE npyv_
{ return _mm512_load_si512((const __m512i*)ptr); } \
NPY_FINLINE npyv_
{ return npyv__loads(ptr); } \
NPY_FINLINE npyv_
{ return npyv__loadl((const __m256i *)ptr); } \
NPY_FINLINE void npyv_store_
{ _mm512_storeu_si512((__m512i*)ptr, vec); } \
NPY_FINLINE void npyv_storea_
{ _mm512_store_si512((__m512i*)ptr, vec); } \
NPY_FINLINE void npyv_stores_
{ _mm512_stream_si512((__m512i*)ptr, vec); } \
NPY_FINLINE void npyv_storel_
{ _mm256_storeu_si256((__m256i*)ptr, npyv512_lower_si256(vec)); } \
NPY_FINLINE void npyv_storeh_
{ _mm256_storeu_si256((__m256i*)(ptr), npyv512_higher_si256(vec)); }
// 定义不同整数类型的 AVX-512 内存操作实现
NPYV_IMPL_AVX512_MEM_INT(npy_uint8, u8)
NPYV_IMPL_AVX512_MEM_INT(npy_int8, s8)
NPYV_IMPL_AVX512_MEM_INT(npy_uint16, u16)
NPYV_IMPL_AVX512_MEM_INT(npy_int16, s16)
NPYV_IMPL_AVX512_MEM_INT(npy_uint32, u32)
NPYV_IMPL_AVX512_MEM_INT(npy_int32, s32)
NPYV_IMPL_AVX512_MEM_INT(npy_uint64, u64)
NPYV_IMPL_AVX512_MEM_INT(npy_int64, s64)
// 不对齐加载操作宏定义
// 对齐加载操作
// 定义宏:以 32 位浮点数精度加载数据到 AVX-512 向量,使用非对齐方式
// 定义宏:以 64 位浮点数精度加载数据到 AVX-512 向量,使用非对齐方式
// 宏条件编译:加载低位部分的数据,根据编译器和架构的不同使用不同的实现
// 定义宏:使用流加载方式加载数据到 AVX-512 向量,适合连续加载操作
// 定义宏:以非对齐方式将 AVX-512 向量中的数据存储到内存
// 定义宏:以对齐方式将 AVX-512 向量中的数据存储到内存
// 定义宏:使用流存储方式将 AVX-512 向量中的数据存储到内存
// 定义宏:存储 AVX-512 向量的低位部分到内存
// 定义宏:存储 AVX-512 向量的高位部分到内存
/***************************
* 非连续加载操作
***************************/
//// 32 位整数加载
// 加载非连续的 32 位无符号整数到 AVX-512 向量
NPY_FINLINE npyv_u32 npyv_loadn_u32(const npy_uint32 *ptr, npy_intp stride)
{
// 断言:步长的绝对值不超过 NPY_SIMD_MAXLOAD_STRIDE32
assert(llabs(stride) <= NPY_SIMD_MAXLOAD_STRIDE32);
// 设置步长向量
const __m512i steps = npyv_set_s32(
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
);
// 计算索引
const __m512i idx = _mm512_mullo_epi32(steps, _mm512_set1_epi32((int)stride));
// 使用步长向量从内存中加载数据到 AVX-512 向量
return _mm512_i32gather_epi32(idx, (const __m512i*)ptr, 4);
}
// 加载非连续的 32 位有符号整数到 AVX-512 向量
NPY_FINLINE npyv_s32 npyv_loadn_s32(const npy_int32 *ptr, npy_intp stride)
{ return npyv_loadn_u32((const npy_uint32*)ptr, stride); }
// 加载非连续的 32 位浮点数到 AVX-512 向量
NPY_FINLINE npyv_f32 npyv_loadn_f32(const float *ptr, npy_intp stride)
{ return _mm512_castsi512_ps(npyv_loadn_u32((const npy_uint32*)ptr, stride)); }
//// 64 位整数加载
// 加载非连续的 64 位无符号整数到 AVX-512 向量
NPY_FINLINE npyv_u64 npyv_loadn_u64(const npy_uint64 *ptr, npy_intp stride)
{
// 设置索引向量
const __m512i idx = npyv_set_s64(
0*stride, 1*stride, 2*stride, 3*stride,
4*stride, 5*stride, 6*stride, 7*stride
);
// 使用索引向量从内存中加载数据到 AVX-512 向量
return _mm512_i64gather_epi64(idx, (const __m512i*)ptr, 8);
}
// 加载非连续的 64 位有符号整数到 AVX-512 向量
NPY_FINLINE npyv_s64 npyv_loadn_s64(const npy_int64 *ptr, npy_intp stride)
{ return npyv_loadn_u64((const npy_uint64*)ptr, stride); }
// 加载非连续的 64 位双精度浮点数到 AVX-512 向量
NPY_FINLINE npyv_f64 npyv_loadn_f64(const double *ptr, npy_intp stride)
{ return _mm512_castsi512_pd(npyv_loadn_u64((const npy_uint64*)ptr, stride)); }
//// 64 位整数加载(通过 32 位步长)
// 加载非连续的 32 位无符号整数到 AVX-512 向量的高位
NPY_FINLINE npyv_u32 npyv_loadn2_u32(const npy_uint32 *ptr, npy_intp stride)
{
// 使用 AVX 指令集加载第二部分数据到 AVX-512 向量的低位
__m128d a = _mm_loadh_pd(
_mm_castsi128_pd(_mm_loadl_epi64((const __m128i*)ptr)),
(const double*)(ptr + stride)
);
//
__m128d b = _mm_loadh_pd(
_mm_castsi128_pd(_mm_loadl_epi64((const __m128i*)(ptr + stride*2))),
(const double*)(ptr + stride*3)
);
__m128d c = _mm_loadh_pd(
_mm_castsi128_pd(_mm_loadl_epi64((const __m128i*)(ptr + stride*4))),
(const double*)(ptr + stride*5)
);
__m128d d = _mm_loadh_pd(
_mm_castsi128_pd(_mm_loadl_epi64((const __m128i*)(ptr + stride*6))),
(const double*)(ptr + stride*7)
);
return _mm512_castpd_si512(npyv512_combine_pd256(
_mm256_insertf128_pd(_mm256_castpd128_pd256(a), b, 1),
_mm256_insertf128_pd(_mm256_castpd128_pd256(c), d, 1)
));
//// 64-bit store over 32-bit stride
NPY_FINLINE void npyv_storen2_u32(npy_uint32 *ptr, npy_intp stride, npyv_u32 a)
{
// 将输入的512位整数向量转换为两个256位双精度浮点向量
__m256d lo = _mm512_castpd512_pd256(_mm512_castsi512_pd(a));
// 提取512位整数向量的高128位并转换为256位双精度浮点向量
__m256d hi = _mm512_extractf64x4_pd(_mm512_castsi512_pd(a), 1);
// 从lo中提取低128位双精度浮点数并存储到ptr的0号元素位置
__m128d e0 = _mm256_castpd256_pd128(lo);
// 从lo中提取高128位双精度浮点数并存储到ptr的1号元素位置
__m128d e1 = _mm256_extractf128_pd(lo, 1);
// 从hi中提取低128位双精度浮点数并存储到ptr的2号元素位置
__m128d e2 = _mm256_castpd256_pd128(hi);
// 从hi中提取高128位双精度浮点数并存储到ptr的3号元素位置
__m128d e3 = _mm256_extractf128_pd(hi, 1);
// 使用单精度浮点数存储ptr的0号位置
_mm_storel_pd((double*)(ptr + stride * 0), e0);
// 使用单精度浮点数存储ptr的1号位置
_mm_storeh_pd((double*)(ptr + stride * 1), e0);
// 使用单精度浮点数存储ptr的2号位置
_mm_storel_pd((double*)(ptr + stride * 2), e1);
}
// 使用 SSE2 指令集中的 _mm_storeh_pd 函数,将双精度浮点数 e1 的高64位存储到 ptr + stride * 3 处
_mm_storeh_pd((double*)(ptr + stride * 3), e1);
// 使用 SSE2 指令集中的 _mm_storel_pd 函数,将双精度浮点数 e2 的低64位存储到 ptr + stride * 4 处
_mm_storel_pd((double*)(ptr + stride * 4), e2);
// 使用 SSE2 指令集中的 _mm_storeh_pd 函数,将双精度浮点数 e2 的高64位存储到 ptr + stride * 5 处
_mm_storeh_pd((double*)(ptr + stride * 5), e2);
// 使用 SSE2 指令集中的 _mm_storel_pd 函数,将双精度浮点数 e3 的低64位存储到 ptr + stride * 6 处
_mm_storel_pd((double*)(ptr + stride * 6), e3);
// 使用 SSE2 指令集中的 _mm_storeh_pd 函数,将双精度浮点数 e3 的高64位存储到 ptr + stride * 7 处
_mm_storeh_pd((double*)(ptr + stride * 7), e3);
//// 128-bit store over 64-bit stride
NPY_FINLINE void npyv_storen2_u64(npy_uint64 *ptr, npy_intp stride, npyv_u64 a)
{
// 将512位无符号整数向量a分成低256位和高256位
__m256i lo = npyv512_lower_si256(a);
__m256i hi = npyv512_higher_si256(a);
// 从lo中提取128位元素e0和e1,从hi中提取128位元素e2和e3
__m128i e0 = _mm256_castsi256_si128(lo);
__m128i e1 = _mm256_extracti128_si256(lo, 1);
__m128i e2 = _mm256_castsi256_si128(hi);
__m128i e3 = _mm256_extracti128_si256(hi, 1);
// 将128位元素e0、e1、e2、e3分别存储到ptr的不同偏移位置
_mm_storeu_si128((__m128i*)(ptr + stride * 0), e0);
_mm_storeu_si128((__m128i*)(ptr + stride * 1), e1);
_mm_storeu_si128((__m128i*)(ptr + stride * 2), e2);
_mm_storeu_si128((__m128i*)(ptr + stride * 3), e3);
}
/*********************************
* Partial Load
*********************************/
//// 32
NPY_FINLINE npyv_s32 npyv_load_till_s32(const npy_int32 *ptr, npy_uintp nlane, npy_int32 fill)
{
// 断言nlane大于0,即加载的元素数量大于0
assert(nlane > 0);
// 设置填充值为vfill
const __m512i vfill = _mm512_set1_epi32(fill);
// 计算掩码,mask为-1或者(1 << nlane) - 1,取决于nlane是否大于15
const __mmask16 mask = nlane > 15 ? -1 : (1 << nlane) - 1;
// 使用掩码加载元素到ret中,ptr强制转换为__m512i类型
__m512i ret = _mm512_mask_loadu_epi32(vfill, mask, (const __m512i*)ptr);
// 如果定义了NPY_SIMD_GUARD_PARTIAL_LOAD,执行下面的工作区绕过
volatile __m512i workaround = ret;
ret = _mm512_or_si512(workaround, ret);
return ret;
}
// fill zero to rest lanes
//// 32
NPY_FINLINE npyv_s32 npyv_load_tillz_s32(const npy_int32 *ptr, npy_uintp nlane)
{
// 断言nlane大于0,即加载的元素数量大于0
assert(nlane > 0);
// 计算掩码,mask为-1或者(1 << nlane) - 1,取决于nlane是否大于15
const __mmask16 mask = nlane > 15 ? -1 : (1 << nlane) - 1;
// 使用掩码加载元素到ret中,ptr强制转换为__m512i类型,加载时填充零
__m512i ret = _mm512_maskz_loadu_epi32(mask, (const __m512i*)ptr);
// 如果定义了NPY_SIMD_GUARD_PARTIAL_LOAD,执行下面的工作区绕过
volatile __m512i workaround = ret;
ret = _mm512_or_si512(workaround, ret);
return ret;
}
//// 64
NPY_FINLINE npyv_s64 npyv_load_till_s64(const npy_int64 *ptr, npy_uintp nlane, npy_int64 fill)
{
// 断言nlane大于0,即加载的元素数量大于0
assert(nlane > 0);
// 设置填充值为vfill
const __m512i vfill = npyv_setall_s64(fill);
// 计算掩码,mask为-1或者(1 << nlane) - 1,取决于nlane是否大于7
const __mmask8 mask = nlane > 7 ? -1 : (1 << nlane) - 1;
// 使用掩码加载元素到ret中,ptr强制转换为__m512i类型
__m512i ret = _mm512_mask_loadu_epi64(vfill, mask, (const __m512i*)ptr);
// 如果定义了NPY_SIMD_GUARD_PARTIAL_LOAD,执行下面的工作区绕过
volatile __m512i workaround = ret;
ret = _mm512_or_si512(workaround, ret);
return ret;
}
// fill zero to rest lanes
//// 64
NPY_FINLINE npyv_s64 npyv_load_tillz_s64(const npy_int64 *ptr, npy_uintp nlane)
{
// 断言nlane大于0,即加载的元素数量大于0
assert(nlane > 0);
// 计算掩码,mask为-1或者(1 << nlane) - 1,取决于nlane是否大于7
const __mmask8 mask = nlane > 7 ? -1 : (1 << nlane) - 1;
// 使用掩码加载元素到ret中,ptr强制转换为__m512i类型,加载时填充零
__m512i ret = _mm512_maskz_loadu_epi64(mask, (const __m512i*)ptr);
// 如果定义了NPY_SIMD_GUARD_PARTIAL_LOAD,执行下面的工作区绕过
volatile __m512i workaround = ret;
ret = _mm512_or_si512(workaround, ret);
return ret;
}
//// 64-bit nlane
// 定义一个函数,加载指定数量的 32 位有符号整数到 SIMD 向量中,同时使用指定的值填充未加载的部分
NPY_FINLINE npyv_s32 npyv_load2_till_s32(const npy_int32 *ptr, npy_uintp nlane,
npy_int32 fill_lo, npy_int32 fill_hi)
{
// 断言确保加载的 lane 数大于 0
assert(nlane > 0);
// 创建一个包含填充值的 512 位整数向量,顺序为 fill_hi, fill_lo, fill_hi, fill_lo
const __m512i vfill = _mm512_set4_epi32(fill_hi, fill_lo, fill_hi, fill_lo);
// 根据 nlane 的大小设置掩码,如果 nlane 大于 7,则掩码为全 1,否则为低 nlane 位为 1
const __mmask8 mask = nlane > 7 ? -1 : (1 << nlane) - 1;
// 使用掩码从指针处加载 64 位整数数据到 512 位整数向量 ret 中
__m512i ret = _mm512_mask_loadu_epi64(vfill, mask, (const __m512i*)ptr);
// 如果定义了 NPY_SIMD_GUARD_PARTIAL_LOAD,执行偏移量加载后的补救措施
volatile __m512i workaround = ret;
ret = _mm512_or_si512(workaround, ret);
// 返回加载后的向量 ret
return ret;
}
// 使用零值填充未加载的 lane
NPY_FINLINE npyv_s32 npyv_load2_tillz_s32(const npy_int32 *ptr, npy_uintp nlane)
{ return npyv_load_tillz_s64((const npy_int64*)ptr, nlane); }
//// 128-bit nlane
// 定义一个函数,加载指定数量的 64 位有符号整数到 SIMD 向量中,同时使用指定的值填充未加载的部分
NPY_FINLINE npyv_u64 npyv_load2_till_s64(const npy_int64 *ptr, npy_uintp nlane,
npy_int64 fill_lo, npy_int64 fill_hi)
{
// 断言确保加载的 lane 数大于 0
assert(nlane > 0);
// 创建一个包含填充值的 512 位整数向量,顺序为 fill_hi, fill_lo, fill_hi, fill_lo
const __m512i vfill = _mm512_set4_epi64(fill_hi, fill_lo, fill_hi, fill_lo);
// 根据 nlane 的大小设置掩码,如果 nlane 大于 3,则掩码为全 1,否则为低 nlane*2 位为 1
const __mmask8 mask = nlane > 3 ? -1 : (1 << (nlane*2)) - 1;
// 使用掩码从指针处加载 64 位整数数据到 512 位整数向量 ret 中
__m512i ret = _mm512_mask_loadu_epi64(vfill, mask, (const __m512i*)ptr);
// 如果定义了 NPY_SIMD_GUARD_PARTIAL_LOAD,执行偏移量加载后的补救措施
volatile __m512i workaround = ret;
ret = _mm512_or_si512(workaround, ret);
// 返回加载后的向量 ret
return ret;
}
// 使用零值填充未加载的 lane
NPY_FINLINE npyv_s64 npyv_load2_tillz_s64(const npy_int64 *ptr, npy_uintp nlane)
{
// 断言确保加载的 lane 数大于 0
assert(nlane > 0);
// 根据 nlane 的大小设置掩码,如果 nlane 大于 3,则掩码为全 1,否则为低 nlane*2 位为 1
const __mmask8 mask = nlane > 3 ? -1 : (1 << (nlane*2)) - 1;
// 使用掩码从指针处加载零填充的 64 位整数数据到 512 位整数向量 ret 中
__m512i ret = _mm512_maskz_loadu_epi64(mask, (const __m512i*)ptr);
// 如果定义了 NPY_SIMD_GUARD_PARTIAL_LOAD,执行偏移量加载后的补救措施
volatile __m512i workaround = ret;
ret = _mm512_or_si512(workaround, ret);
// 返回加载后的向量 ret
return ret;
}
/*********************************
* Non-contiguous partial load
*********************************/
//// 32
// 定义一个函数,以非连续的方式加载指定数量的 32 位有符号整数到 SIMD 向量中,同时使用指定的值填充未加载的部分
NPY_FINLINE npyv_s32
npyv_loadn_till_s32(const npy_int32 *ptr, npy_intp stride, npy_uintp nlane, npy_int32 fill)
{
// 断言确保加载的 lane 数大于 0
assert(nlane > 0);
// 断言确保步长的绝对值不超过 NPY_SIMD_MAXLOAD_STRIDE32
assert(llabs(stride) <= NPY_SIMD_MAXLOAD_STRIDE32);
// 创建一个顺序为 0 到 15 的 512 位整数向量 steps
const __m512i steps = npyv_set_s32(
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
);
// 使用步长和给定的步长值计算索引
const __m512i idx = _mm512_mullo_epi32(steps, _mm512_set1_epi32((int)stride));
// 创建一个包含填充值的 512 位整数向量 vfill
const __m512i vfill = _mm512_set1_epi32(fill);
// 根据 nlane 的大小设置掩码,如果 nlane 大于 15,则掩码为全 1,否则为低 nlane 位为 1
const __mmask16 mask = nlane > 15 ? -1 : (1 << nlane) - 1;
// 使用掩码从 ptr 指针处按照 4 字节步长加载 32 位整数数据到 512 位整数向量 ret 中
__m512i ret = _mm512_mask_i32gather_epi32(vfill, mask, idx, (const __m512i*)ptr, 4);
// 如果定义了 NPY_SIMD_GUARD_PARTIAL_LOAD,执行偏移量加载后的补救措施
volatile __m512i workaround = ret;
ret = _mm512_or_si512(workaround, ret);
// 返回加载后的向量 ret
return ret;
}
// 使用零值填充未加载的 lane
NPY_FINLINE npyv_s32
npyv_loadn_tillz_s32(const npy_int32 *ptr, npy_intp stride, npy_uintp nlane)
{ return npyv_loadn_till_s32(ptr, stride, nlane, 0); }
//// 64
// 定义一个函数,以非连续的方式加载指定数量的 64 位有符号整数到 SIMD 向量中,同时使用指定的值填充未加载的部分
NPY_FINLINE npyv_s64
npyv_loadn_till_s64(const npy_int64 *ptr, npy_intp stride, npy_uintp nlane, npy_int64 fill)
{
// 断言确保加载的 lane 数大于 0
assert(nlane > 0);
// 断言确保步长的绝对值不超过 NPY_SIMD_MAXLOAD_STRIDE64
assert(llabs(stride) <= NPY_SIMD_MAXLOAD_STRIDE64);
//cpp
// 创建一个包含给定索引值的 __m512i 向量,这些索引是以步长 stride 递增的
const __m512i idx = npyv_set_s64(
0*stride, 1*stride, 2*stride, 3*stride,
4*stride, 5*stride, 6*stride, 7*stride
);
// 创建一个所有元素均为 fill 的 __m512i 向量
const __m512i vfill = npyv_setall_s64(fill);
// 根据 nlane 的值创建一个掩码,如果 nlane 大于 15,则掩码设置为全1;否则设置为 (1 << nlane) - 1
const __mmask8 mask = nlane > 15 ? -1 : (1 << nlane) - 1;
// 使用 _mm512_mask_i64gather_epi64 函数从 ptr 指向的内存中根据 idx 向量的索引收集数据到 ret 向量中,每次收集 8 个元素
__m512i ret = _mm512_mask_i64gather_epi64(vfill, mask, idx, (const __m512i*)ptr, 8);
// 如果定义了 NPY_SIMD_GUARD_PARTIAL_LOAD 宏,则使用 workaround 来处理 ret
volatile __m512i workaround = ret;
ret = _mm512_or_si512(workaround, ret);
// 返回 ret 变量的值
return ret;
}
// 使用零值填充其余的通道
//// 64-bit load over 32-bit stride
// 以 64 位加载数据,步长为 32 位
NPY_FINLINE npyv_s64 npyv_loadn2_till_s32(const npy_int32 *ptr, npy_intp stride, npy_uintp nlane,
npy_int32 fill_lo, npy_int32 fill_hi)
{
assert(nlane > 0);
// 创建包含索引的 __m512i 对象,每个索引乘以步长用于加载数据
const __m512i idx = npyv_set_s64(
0*stride, 1*stride, 2*stride, 3*stride,
4*stride, 5*stride, 6*stride, 7*stride
);
// 创建包含填充值的 __m512i 对象
const __m512i vfill = _mm512_set4_epi32(fill_hi, fill_lo, fill_hi, fill_lo);
// 根据 nlane 的大小设置掩码,以便决定加载多少数据
const __mmask8 mask = nlane > 7 ? -1 : (1 << nlane) - 1;
// 使用 gather 操作加载数据到 ret 变量中
__m512i ret = _mm512_mask_i64gather_epi64(vfill, mask, idx, (const __m512i*)ptr, 4);
// 如果定义了 NPY_SIMD_GUARD_PARTIAL_LOAD 宏,则使用 workaround 处理 ret
volatile __m512i workaround = ret;
ret = _mm512_or_si512(workaround, ret);
// 返回加载的结果
return ret;
}
// 使用零值填充其余的通道
//// 128-bit load over 64-bit stride
// 以 128 位加载数据,步长为 64 位
NPY_FINLINE npyv_s64 npyv_loadn2_till_s64(const npy_int64 *ptr, npy_intp stride, npy_uintp nlane,
npy_int64 fill_lo, npy_int64 fill_hi)
{
assert(nlane > 0);
// 创建包含索引的 __m512i 对象,每个索引乘以步长用于加载数据
const __m512i idx = npyv_set_s64(
0, 1, stride, stride+1,
stride*2, stride*2+1, stride*3, stride*3+1
);
// 根据 nlane 的大小设置掩码,以便决定加载多少数据
const __mmask8 mask = nlane > 3 ? -1 : (1 << (nlane*2)) - 1;
// 创建包含填充值的 __m512i 对象
const __m512i vfill = _mm512_set4_epi64(fill_hi, fill_lo, fill_hi, fill_lo);
// 使用 gather 操作加载数据到 ret 变量中
__m512i ret = _mm512_mask_i64gather_epi64(vfill, mask, idx, (const __m512i*)ptr, 8);
// 如果定义了 NPY_SIMD_GUARD_PARTIAL_LOAD 宏,则使用 workaround 处理 ret
volatile __m512i workaround = ret;
ret = _mm512_or_si512(workaround, ret);
// 返回加载的结果
return ret;
}
// 使用零值填充其余的通道
/*********************************
* Partial store
*********************************/
//// 32
// 存储至少 32 位数据
NPY_FINLINE void npyv_store_till_s32(npy_int32 *ptr, npy_uintp nlane, npyv_s32 a)
{
assert(nlane > 0);
// 根据 nlane 的大小设置掩码,以便决定存储多少数据
const __mmask16 mask = nlane > 15 ? -1 : (1 << nlane) - 1;
// 使用掩码进行存储操作
_mm512_mask_storeu_epi32((__m512i*)ptr, mask, a);
}
//// 64
// 存储至少 64 位数据
NPY_FINLINE void npyv_store_till_s64(npy_int64 *ptr, npy_uintp nlane, npyv_s64 a)
{
assert(nlane > 0);
// 根据 nlane 的大小设置掩码,以便决定存储多少数据
const __mmask8 mask = nlane > 7 ? -1 : (1 << nlane) - 1;
// 使用掩码进行存储操作
_mm512_mask_storeu_epi64((__m512i*)ptr, mask, a);
}
//// 64-bit nlane
// 存储至少 64 位数据
NPY_FINLINE void npyv_store2_till_s32(npy_int32 *ptr, npy_uintp nlane, npyv_s32 a)
{
assert(nlane > 0);
// 根据 nlane 的大小设置掩码,以便决定存储多少数据
const __mmask8 mask = nlane > 7 ? -1 : (1 << nlane) - 1;
_mm512_mask_storeu_epi64((__m512i*)ptr, mask, a);
/*********************************
* Non-contiguous partial store
*********************************/
//// 32
NPY_FINLINE void npyv_storen_till_s32(npy_int32 *ptr, npy_intp stride, npy_uintp nlane, npyv_s32 a)
{
// 确保要存储的元素数量大于0
assert(nlane > 0);
// 确保步长的绝对值不超过最大存储步长限制
assert(llabs(stride) <= NPY_SIMD_MAXSTORE_STRIDE32);
// 创建一个步长数组
const __m512i steps = _mm512_setr_epi32(
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
);
// 计算实际存储时的索引,使用给定的步长
const __m512i idx = _mm512_mullo_epi32(steps, _mm512_set1_epi32((int)stride));
// 根据要存储的元素数量,创建一个掩码
const __mmask16 mask = nlane > 15 ? -1 : (1 << nlane) - 1;
// 使用掩码进行非连续的整数存储
_mm512_mask_i32scatter_epi32((__m512i*)ptr, mask, idx, a, 4);
}
//// 64
NPY_FINLINE void npyv_storen_till_s64(npy_int64 *ptr, npy_intp stride, npy_uintp nlane, npyv_s64 a)
{
// 确保要存储的元素数量大于0
assert(nlane > 0);
// 创建一个步长数组
const __m512i idx = npyv_set_s64(
0*stride, 1*stride, 2*stride, 3*stride,
4*stride, 5*stride, 6*stride, 7*stride
);
// 根据要存储的元素数量,创建一个掩码
const __mmask8 mask = nlane > 7 ? -1 : (1 << nlane) - 1;
// 使用掩码进行非连续的64位整数存储
_mm512_mask_i64scatter_epi64((__m512i*)ptr, mask, idx, a, 8);
}
//// 64-bit store over 32-bit stride
NPY_FINLINE void npyv_storen2_till_s32(npy_int32 *ptr, npy_intp stride, npy_uintp nlane, npyv_s32 a)
{
// 确保要存储的元素数量大于0
assert(nlane > 0);
// 创建一个步长数组
const __m512i idx = npyv_set_s64(
0*stride, 1*stride, 2*stride, 3*stride,
4*stride, 5*stride, 6*stride, 7*stride
);
// 根据要存储的元素数量,创建一个掩码
const __mmask8 mask = nlane > 7 ? -1 : (1 << nlane) - 1;
// 使用掩码进行64位整数存储,但每个元素占4字节步长
_mm512_mask_i64scatter_epi64((__m512i*)ptr, mask, idx, a, 4);
}
//// 128-bit store over 64-bit stride
NPY_FINLINE void npyv_storen2_till_s64(npy_int64 *ptr, npy_intp stride, npy_uintp nlane, npyv_s64 a)
{
// 确保要存储的元素数量大于0
assert(nlane > 0);
// 创建一个步长数组
const __m512i idx = npyv_set_s64(
0, 1, stride, stride+1,
2*stride, 2*stride+1, 3*stride, 3*stride+1
);
// 根据要存储的元素数量,创建一个掩码
const __mmask8 mask = nlane > 3 ? -1 : (1 << (nlane*2)) - 1;
// 使用掩码进行64位整数存储,每个元素占8字节步长
_mm512_mask_i64scatter_epi64((__m512i*)ptr, mask, idx, a, 8);
}
{ \
union { \
npyv_lanetype_
npyv_lanetype_
} pun; \ // 声明 pun 作为联合体变量
pun.from_
return npyv_reinterpret_
(const npyv_lanetype_
)); \ // 返回 npyv_reinterpret_
} \ // 结束函数定义
NPY_FINLINE npyv_
(const npyv_lanetype_
npyv_lanetype_
{ \
union { \
npyv_lanetype_
npyv_lanetype_
} pun; \ // 声明 pun 作为联合体变量
pun.from_
return npyv_reinterpret_
(const npyv_lanetype_
)); \ // 返回 npyv_reinterpret_
} \ // 结束函数定义
NPY_FINLINE npyv_
(const npyv_lanetype_
{ \
return npyv_reinterpret_
(const npyv_lanetype_
)); \ // 返回 npyv_reinterpret_
} \ // 结束函数定义
NPY_FINLINE npyv_
(const npyv_lanetype_
{
return npyv_reinterpret_
(const npyv_lanetype_
));
}
NPY_FINLINE void npyv_store_till_
(npyv_lanetype_
{
npyv_store_till_
(npyv_lanetype_
npyv_reinterpret_
);
}
NPY_FINLINE void npyv_storen_till_
(npyv_lanetype_
{
npyv_storen_till_
(npyv_lanetype_
npyv_reinterpret_
);
}
// 定义AVX-512加速部分类型的宏,用于u32和s32类型
NPYV_IMPL_AVX512_REST_PARTIAL_TYPES(u32, s32)
// 定义AVX-512加速部分类型的宏,用于f32和s32类型
NPYV_IMPL_AVX512_REST_PARTIAL_TYPES(f32, s32)
// 定义AVX-512加速部分类型的宏,用于u64和s64类型
NPYV_IMPL_AVX512_REST_PARTIAL_TYPES(u64, s64)
// 定义AVX-512加速部分类型的宏,用于f64和s64类型
// 128位/64位步长(双元素加载/存储)的宏定义
// 内部联合结构用于类型转换
NPY_FINLINE npyv_
(const npyv_lanetype_
npyv_lanetype_
{ \
// 联合结构定义
union pun { \
npyv_lanetype_
npyv_lanetype_
}; \
// 低位和高位数据的联合结构变量
union pun pun_lo; \
union pun pun_hi; \
// 将填充的低位和高位数据存入联合结构中
pun_lo.from_
pun_hi.from_
// 调用加载函数,将转换后的数据作为参数传递
return npyv_reinterpret_
(const npyv_lanetype_
)); \
} \
// 内部联合结构用于类型转换
NPY_FINLINE npyv_
(const npyv_lanetype_
npyv_lanetype_
{ \
union pun { \
npyv_lanetype_
npyv_lanetype_
}; \
union pun pun_lo; \
union pun pun_hi; \
pun_lo.from_
pun_hi.from_
return npyv_reinterpret_
(const npyv_lanetype_
pun_hi.to_
)); \
} \
// 定义一个内联函数 npyv_load2_tillz_
NPY_FINLINE npyv_
(const npyv_lanetype_
{ \
// 调用相应的加载函数,将输入指针的数据加载并转换为目标类型
return npyv_reinterpret_
(const npyv_lanetype_
)); \
} \
// 定义一个内联函数 npyv_loadn2_tillz_
NPY_FINLINE npyv_
(const npyv_lanetype_
{ \
// 调用相应的加载函数,将输入指针的数据加载并转换为目标类型,带有指定步长和数量
return npyv_reinterpret_
(const npyv_lanetype_
)); \
} \
// 定义一个内联函数 npyv_store2_till_
NPY_FINLINE void npyv_store2_till_
(npyv_lanetype_
{
npyv_store2_till_
(npyv_lanetype_
npyv_reinterpret_
);
}
NPY_FINLINE void npyv_storen2_till_
(npyv_lanetype_
{
npyv_storen2_till_
(npyv_lanetype_
npyv_reinterpret_
);
}
{
// 使用 `npyv_store2_till_
npyv_store2_till_
(npyv_lanetype_
npyv_reinterpret_
);
}
// 使用 `npyv_storen2_till_
NPY_FINLINE void npyv_storen2_till_
(npyv_lanetype_
{
npyv_storen2_till_
(npyv_lanetype_
npyv_reinterpret_
);
}
// 宏定义,用于生成 AVX-512 指令集下的加载和存储操作的代码片段,支持两种数据类型的成对操作
// 在 AVX-512 指令集下实现 TYPE1 和 TYPE2 数据类型的特定操作
// 宏定义,实现 AVX-512 指令集下的内存交织加载和存储操作
// 定义函数 npyv_zip_
NPY_FINLINE npyv_
NPY_FINLINE npyv_
// 定义函数 npyv_load_
NPY_FINLINE npyv_
const npyv_lanetype_
) { \
return npyv_unzip_
npyv_load_
); \
} \
// 定义函数 npyv_store_
NPY_FINLINE void npyv_store_
npyv_lanetype_
) { \
npyv_
npyv_store_
npyv_store_
}
// 使用宏定义生成 AVX-512 指令集下的内存交织加载和存储操作的具体实现
NPYV_IMPL_AVX512_MEM_INTERLEAVE(u8, u8)
NPYV_IMPL_AVX512_MEM_INTERLEAVE(s8, u8)
NPYV_IMPL_AVX512_MEM_INTERLEAVE(u16, u16)
NPYV_IMPL_AVX512_MEM_INTERLEAVE(s16, u16)
NPYV_IMPL_AVX512_MEM_INTERLEAVE(u32, u32)
NPYV_IMPL_AVX512_MEM_INTERLEAVE(s32, u32)
NPYV_IMPL_AVX512_MEM_INTERLEAVE(u64, u64)
NPYV_IMPL_AVX512_MEM_INTERLEAVE(s64, u64)
NPYV_IMPL_AVX512_MEM_INTERLEAVE(f32, f32)
NPYV_IMPL_AVX512_MEM_INTERLEAVE(f64, f64)
/**************************************************
* Lookup table
*************************************************/
// 使用向量作为索引来访问包含 32 个 float32 元素的查找表
NPY_FINLINE npyv_f32 npyv_lut32_f32(const float *table, npyv_u32 idx)
{
// 加载表中的前 16 个元素到向量 table0
const npyv_f32 table0 = npyv_load_f32(table);
// 加载表中的后 16 个元素到向量 table1
const npyv_f32 table1 = npyv_load_f32(table + 16);
// 使用 _mm512_permutex2var_ps 函数按照 idx 的指定顺序对 table0 和 table1 进行混洗操作
return _mm512_permutex2var_ps(table0, idx, table1);
}
// 使用向量作为索引来访问包含 32 个元素的 uint32 查找表,并将结果转换为 float32 向量返回
NPY_FINLINE npyv_u32 npyv_lut32_u32(const npy_uint32 *table, npyv_u32 idx)
{ return npyv_reinterpret_u32_f32(npyv_lut32_f32((const float*)table, idx)); }
// 使用向量作为索引来访问包含 32 个元素的 int32 查找表,并将结果转换为 float32 向量返回
NPY_FINLINE npyv_s32 npyv_lut32_s32(const npy_int32 *table, npyv_u32 idx)
{ return npyv_reinterpret_s32_f32(npyv_lut32_f32((const float*)table, idx)); }
// 使用向量作为索引来访问包含 16 个元素的 float64 查找表
// (此处代码截断,未完成)
const npyv_f64 table0 = npyv_load_f64(table);
const npyv_f64 table1 = npyv_load_f64(table + 8);
return _mm512_permutex2var_pd(table0, idx, table1);
// 返回一个npyv_u64类型的向量,其中根据给定的索引从64位整数表中查找值
NPY_FINLINE npyv_u64 npyv_lut16_u64(const npy_uint64 *table, npyv_u64 idx)
{ return npyv_reinterpret_u64_f64(npyv_lut16_f64((const double*)table, idx)); }
// 返回一个npyv_s64类型的向量,其中根据给定的索引从64位有符号整数表中查找值
NPY_FINLINE npyv_s64 npyv_lut16_s64(const npy_int64 *table, npyv_u64 idx)
{ return npyv_reinterpret_s64_f64(npyv_lut16_f64((const double*)table, idx)); }
.\numpy\numpy\_core\src\common\simd\avx512\misc.h
// 定义了一系列宏用于将 AVX-512 向量的所有元素设置为零
// 定义了一系列宏用于将 AVX-512 向量的所有元素设置为相同的值
// 在一些编译器中缺少了 _mm512_set_epi8 和 _mm512_set_epi16 函数,这里定义了一个特定值的向量函数
NPY_FINLINE __m512i npyv__setr_epi64(
npy_int64, npy_int64, npy_int64, npy_int64,
npy_int64, npy_int64, npy_int64, npy_int64
);
// 设置所有 AVX-512 64 位无符号整数向量的元素为同一个值
NPY_FINLINE npyv_u64 npyv_setall_u64(npy_uint64 a)
{
npy_int64 ai = (npy_int64)a;
return npyv__setr_epi64(ai, ai, ai, ai, ai, ai, ai, ai);
return _mm512_set1_epi64(ai);
}
// 设置所有 AVX-512 64 位有符号整数向量的元素为同一个值
NPY_FINLINE npyv_s64 npyv_setall_s64(npy_int64 a)
{
return npyv__setr_epi64(a, a, a, a, a, a, a, a);
return _mm512_set1_epi64(a);
}
/**
* 设置 AVX-512 8 位整数向量的每个通道为特定值,并将其余通道设置为特定值
*
* 在许多编译器中缺少了 _mm512_set_epi8 和 _mm512_set_epi16 函数
*/
NPY_FINLINE __m512i npyv__setr_epi8(
char i0, char i1, char i2, char i3, char i4, char i5, char i6, char i7,
char i8, char i9, char i10, char i11, char i12, char i13, char i14, char i15,
char i16, char i17, char i18, char i19, char i20, char i21, char i22, char i23,
char i24, char i25, char i26, char i27, char i28, char i29, char i30, char i31,
char i32, char i33, char i34, char i35, char i36, char i37, char i38, char i39,
char i40, char i41, char i42, char i43, char i44, char i45, char i46, char i47,
char i48, char i49, char i50, char i51, char i52, char i53, char i54, char i55,
char i56, char i57, char i58, char i59, char i60, char i61, char i62, char i63)
{
// 将输入的字符数组按照 AVX-512 要求对齐后加载为向量
const char NPY_DECL_ALIGNED(64) data[64] = {
i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15,
i16, i17, i18, i19, i20, i21, i22, i23, i24, i25, i26, i27, i28, i29, i30, i31,
i32, i33, i34, i35, i36, i37, i38, i39, i40, i41, i42, i43, i44, i45, i46, i47,
i48, i49, i50, i51, i52, i53, i54, i55, i56, i57, i58, i59, i60, i61, i62, i63
};
// 加载数据到 AVX-512 整数向量
return _mm512_load_si512((const void*)data);
}
NPY_FINLINE __m512i npyv__setr_epi16(
short i0, short i1, short i2, short i3, short i4, short i5, short i6, short i7,
short i8, short i9, short i10, short i11, short i12, short i13, short i14, short i15,
short i16, short i17, short i18, short i19, short i20, short i21, short i22, short i23,
short i24, short i25, short i26, short i27, short i28, short i29, short i30, short i31)
{
// 创建包含32个元素的数组,每个元素是输入的短整型参数
const short NPY_DECL_ALIGNED(64) data[32] = {
i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15,
i16, i17, i18, i19, i20, i21, i22, i23, i24, i25, i26, i27, i28, i29, i30, i31
};
// 使用 AVX-512 指令集加载包含在数组中的数据到 __m512i 类型的寄存器
return _mm512_load_si512((const void*)data);
}
// 如果 _mm512_setr_* 被定义为宏,则由于宏不会展开生成的参数。
NPY_FINLINE __m512i npyv__setr_epi32(
int i0, int i1, int i2, int i3, int i4, int i5, int i6, int i7,
int i8, int i9, int i10, int i11, int i12, int i13, int i14, int i15)
{
// 使用输入的整型参数创建一个 __m512i 类型的寄存器
return _mm512_setr_epi32(i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15);
}
// 使用输入的 64 位整型参数创建一个 __m512i 类型的寄存器
NPY_FINLINE __m512i npyv__setr_epi64(npy_int64 i0, npy_int64 i1, npy_int64 i2, npy_int64 i3,
npy_int64 i4, npy_int64 i5, npy_int64 i6, npy_int64 i7)
{
// 如果编译器为 MSVC 且目标平台为 x86,则使用 _mm512_setr_epi32 创建 __m512i 类型的寄存器
// 否则,使用 _mm512_setr_epi64 创建 __m512i 类型的寄存器
return _mm512_setr_epi32(
(int)i0, (int)(i0 >> 32), (int)i1, (int)(i1 >> 32),
(int)i2, (int)(i2 >> 32), (int)i3, (int)(i3 >> 32),
(int)i4, (int)(i4 >> 32), (int)i5, (int)(i5 >> 32),
(int)i6, (int)(i6 >> 32), (int)i7, (int)(i7 >> 32)
);
return _mm512_setr_epi64(i0, i1, i2, i3, i4, i5, i6, i7);
}
// 使用输入的单精度浮点参数创建一个 __m512 类型的寄存器
NPY_FINLINE __m512 npyv__setr_ps(
float i0, float i1, float i2, float i3, float i4, float i5, float i6, float i7,
float i8, float i9, float i10, float i11, float i12, float i13, float i14, float i15)
{
// 使用 _mm512_setr_ps 创建一个 __m512 类型的寄存器,包含输入的单精度浮点参数
return _mm512_setr_ps(i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15);
}
// 使用输入的双精度浮点参数创建一个 __m512d 类型的寄存器
NPY_FINLINE __m512d npyv__setr_pd(double i0, double i1, double i2, double i3,
double i4, double i5, double i6, double i7)
{
// 使用 _mm512_setr_pd 创建一个 __m512d 类型的寄存器,包含输入的双精度浮点参数
return _mm512_setr_pd(i0, i1, i2, i3, i4, i5, i6, i7);
}
// 宏定义,用于将填充值 FILL 和可变参数展开为对应的 npyv__setr_epi8 函数调用
// 定义宏 npyv_setf_f32,用于设置单精度浮点数向量中的值,调用 npyv__setr_ps 宏来实现
// 定义宏 npyv_setf_f64,用于设置双精度浮点数向量中的值,调用 npyv__setr_pd 宏来实现
// 定义宏 npyv_set_u8,将 npyv_setf_u8 宏调用为设置无符号8位整数向量中的值,0表示其余所有通道为0
// 定义宏 npyv_set_s8,将 npyv_setf_s8 宏调用为设置有符号8位整数向量中的值,0表示其余所有通道为0
// 定义宏 npyv_set_u16,将 npyv_setf_u16 宏调用为设置无符号16位整数向量中的值,0表示其余所有通道为0
// 定义宏 npyv_set_s16,将 npyv_setf_s16 宏调用为设置有符号16位整数向量中的值,0表示其余所有通道为0
// 定义宏 npyv_set_u32,将 npyv_setf_u32 宏调用为设置无符号32位整数向量中的值,0表示其余所有通道为0
// 定义宏 npyv_set_s32,将 npyv_setf_s32 宏调用为设置有符号32位整数向量中的值,0表示其余所有通道为0
// 定义宏 npyv_set_u64,将 npyv_setf_u64 宏调用为设置无符号64位整数向量中的值,0表示其余所有通道为0
// 定义宏 npyv_set_s64,将 npyv_setf_s64 宏调用为设置有符号64位整数向量中的值,0表示其余所有通道为0
// 定义宏 npyv_set_f32,将 npyv_setf_f32 宏调用为设置单精度浮点数向量中的值,0表示其余所有通道为0
// 定义宏 npyv_set_f64,将 npyv_setf_f64 宏调用为设置双精度浮点数向量中的值,0表示其余所有通道为0
// 根据是否支持 AVX512BW 指令集选择不同的宏实现
// 如果支持 AVX512BW,定义宏 npyv_select_u8,使用 _mm512_mask_blend_epi8 实现按位选择无符号8位整数向量
// 定义宏 npyv_select_u16,使用 _mm512_mask_blend_epi16 实现按位选择无符号16位整数向量
// 如果不支持 AVX512BW,定义函数 npyv_select_u8,使用 _mm512_xor_si512 和 _mm512_and_si512 实现按位选择无符号8位整数向量
NPY_FINLINE __m512i npyv_select_u8(__m512i mask, __m512i a, __m512i b)
{ return _mm512_xor_si512(b, _mm512_and_si512(_mm512_xor_si512(b, a), mask)); }
// 定义宏 npyv_select_u16,与 npyv_select_u8 相同
// 定义宏 npyv_select_s8,与 npyv_select_u8 相同
// 定义宏 npyv_select_s16,与 npyv_select_u16 相同
// 定义宏 npyv_select_u32,使用 _mm512_mask_blend_epi32 实现按位选择无符号32位整数向量
// 定义宏 npyv_select_s32,与 npyv_select_u32 相同
// 定义宏 npyv_select_u64,使用 _mm512_mask_blend_epi64 实现按位选择无符号64位整数向量
// 定义宏 npyv_select_s64,与 npyv_select_u64 相同
// 定义宏 npyv_select_f32,使用 _mm512_mask_blend_ps 实现按位选择单精度浮点数向量
// 定义宏 npyv_select_f64,使用 _mm512_mask_blend_pd 实现按位选择双精度浮点数向量
// 提取第一个向量的第一个通道值
// 重新解释宏,将输入参数重新解释为无符号8位整数向量
// 将输入参数重新解释为有符号8位整数向量
// 定义将 512 位整数转换为单精度浮点数的宏,使用 AVX-512 指令集
// 定义将 512 位整数转换为单精度浮点数的宏,使用 AVX-512 指令集
// 定义将 512 位整数转换为单精度浮点数的宏,使用 AVX-512 指令集
// 定义将 512 位整数转换为单精度浮点数的宏,使用 AVX-512 指令集
// 定义将 512 位整数转换为单精度浮点数的宏,使用 AVX-512 指令集
// 定义将 512 位整数转换为单精度浮点数的宏,使用 AVX-512 指令集
// 定义将 512 位整数转换为单精度浮点数的宏,使用 AVX-512 指令集
// 定义将 512 位双精度浮点数转换为单精度浮点数的宏,使用 AVX-512 指令集
// 定义将双精度浮点数保持不变的宏
// 定义将 512 位整数转换为双精度浮点数的宏,使用 AVX-512 指令集
// 定义将 512 位整数转换为双精度浮点数的宏,使用 AVX-512 指令集
// 定义将 512 位整数转换为双精度浮点数的宏,使用 AVX-512 指令集
// 定义将 512 位整数转换为双精度浮点数的宏,使用 AVX-512 指令集
// 定义将 512 位整数转换为双精度浮点数的宏,使用 AVX-512 指令集
// 定义将 512 位整数转换为双精度浮点数的宏,使用 AVX-512 指令集
// 定义将 512 位整数转换为双精度浮点数的宏,使用 AVX-512 指令集
// 定义将 512 位整数转换为双精度浮点数的宏,使用 AVX-512 指令集
// 定义将单精度浮点数转换为双精度浮点数的宏,使用 AVX-512 指令集
// 如果定义了 NPY_HAVE_AVX512_KNL,则定义 npyv_cleanup 为空操作;否则定义为 _mm256_zeroall,该函数清空 AVX 寄存器
// 结束条件编译指令,用于保护头文件内容不被重复引入
.\numpy\numpy\_core\src\common\simd\avx512\operators.h
/***************************
* Shifting
***************************/
// left
// 定义 AVX512BW 不可用时的位移操作函数,参数为 512 位整数向量和整数常量
NPY_FINLINE __m512i npyv_
{ \
// 将 512 位向量拆分为两个 256 位向量
__m256i l = npyv512_lower_si256(a); \
__m256i h = npyv512_higher_si256(a); \
// 将整数常量转换为 128 位的数据类型
__m128i cv = _mm_cvtsi32_si128(c); \
// 对低位和高位向量分别进行指定的位移操作
l = _mm256_
h = _mm256_
// 将结果重新组合成一个 512 位向量返回
return npyv512_combine_si256(l, h); \
}
// 定义具体的位移函数 npyv_shl_u16,使用 AVX2 指令集的 sll_epi16 函数
NPYV_IMPL_AVX512_SHIFT(shl_u16, sll_epi16)
// 定义其他整数类型的左移操作宏,参数为向量和整数常量,使用对应的 AVX512 指令
// left by an immediate constant
// 如果支持 AVX512BW,则使用对应的 slli_epi16 函数进行左移操作
// 否则使用之前定义的 npyv_shl_u16 宏进行左移操作
// 定义其他整数类型的按常量左移操作宏,使用对应的 AVX512 指令
// right
// 如果支持 AVX512BW,则使用对应的 srl_epi16 和 sra_epi16 函数进行右移操作
// 否则使用自定义的宏 NPYV_IMPL_AVX512_SHIFT 定义的右移函数
NPYV_IMPL_AVX512_SHIFT(shr_u16, srl_epi16)
NPYV_IMPL_AVX512_SHIFT(shr_s16, sra_epi16)
// 定义其他整数类型的右移操作宏,使用对应的 AVX512 指令
// right by an immediate constant
// 如果支持 AVX512BW,则使用对应的 srli_epi16 和 srai_epi16 函数进行按常量右移操作
// 否则使用之前定义的 npyv_shr_u16 和 npyv_shr_s16 宏进行右移操作
// 定义其他整数类型的按常量右移操作宏,使用对应的 AVX512 指令
/***************************
* Logical
***************************/
// AND
// 定义各种整数类型的按位与操作宏,使用对应的 AVX512 指令
NPYV_IMPL_AVX512_FROM_SI512_PS_2ARG(npyv_and_f32, _mm512_and_si512)
// 定义 AVX512 浮点数按位与操作宏,使用 _mm512_and_si512 实现
NPYV_IMPL_AVX512_FROM_SI512_PD_2ARG(npyv_and_f64, _mm512_and_si512)
// 定义 AVX512 双精度浮点数按位与操作宏,使用 _mm512_and_si512 实现
// OR
// 定义 AVX512 无符号 8 位整数按位或操作宏,使用 _mm512_or_si512
// 定义 AVX512 有符号 8 位整数按位或操作宏,使用 _mm512_or_si512
// 定义 AVX512 无符号 16 位整数按位或操作宏,使用 _mm512_or_si512
// 定义 AVX512 有符号 16 位整数按位或操作宏,使用 _mm512_or_si512
// 定义 AVX512 无符号 32 位整数按位或操作宏,使用 _mm512_or_si512
// 定义 AVX512 有符号 32 位整数按位或操作宏,使用 _mm512_or_si512
// 定义 AVX512 无符号 64 位整数按位或操作宏,使用 _mm512_or_si512
// 定义 AVX512 有符号 64 位整数按位或操作宏,使用 _mm512_or_si512
// 如果支持 AVX512DQ,定义 AVX512 单精度浮点数按位或操作宏,使用 _mm512_or_ps
// 如果支持 AVX512DQ,定义 AVX512 双精度浮点数按位或操作宏,使用 _mm512_or_pd
NPYV_IMPL_AVX512_FROM_SI512_PS_2ARG(npyv_or_f32, _mm512_or_si512)
// 否则,定义 AVX512 浮点数按位或操作宏,使用 _mm512_or_si512 实现
NPYV_IMPL_AVX512_FROM_SI512_PD_2ARG(npyv_or_f64, _mm512_or_si512)
// 否则,定义 AVX512 双精度浮点数按位或操作宏,使用 _mm512_or_si512 实现
// XOR
// 定义 AVX512 无符号 8 位整数按位异或操作宏,使用 _mm512_xor_si512
// 定义 AVX512 有符号 8 位整数按位异或操作宏,使用 _mm512_xor_si512
// 定义 AVX512 无符号 16 位整数按位异或操作宏,使用 _mm512_xor_si512
// 定义 AVX512 有符号 16 位整数按位异或操作宏,使用 _mm512_xor_si512
// 定义 AVX512 无符号 32 位整数按位异或操作宏,使用 _mm512_xor_si512
// 定义 AVX512 有符号 32 位整数按位异或操作宏,使用 _mm512_xor_si512
// 定义 AVX512 无符号 64 位整数按位异或操作宏,使用 _mm512_xor_si512
// 定义 AVX512 有符号 64 位整数按位异或操作宏,使用 _mm512_xor_si512
// 如果支持 AVX512DQ,定义 AVX512 单精度浮点数按位异或操作宏,使用 _mm512_xor_ps
// 如果支持 AVX512DQ,定义 AVX512 双精度浮点数按位异或操作宏,使用 _mm512_xor_pd
NPYV_IMPL_AVX512_FROM_SI512_PS_2ARG(npyv_xor_f32, _mm512_xor_si512)
// 否则,定义 AVX512 浮点数按位异或操作宏,使用 _mm512_xor_si512 实现
NPYV_IMPL_AVX512_FROM_SI512_PD_2ARG(npyv_xor_f64, _mm512_xor_si512)
// 否则,定义 AVX512 双精度浮点数按位异或操作宏,使用 _mm512_xor_si512 实现
// NOT
// 定义 AVX512 无符号 8 位整数按位取反操作宏,使用 _mm512_xor_si512(A, _mm512_set1_epi32(-1))
// 定义 AVX512 有符号 8 位整数按位取反操作宏,与无符号 8 位整数取反相同
// 定义 AVX512 无符号 16 位整数按位取反操作宏,与无符号 8 位整数取反相同
// 定义 AVX512 有符号 16 位整数按位取反操作宏,与无符号 8 位整数取反相同
// 定义 AVX512 无符号 32 位整数按位取反操作宏,与无符号 8 位整数取反相同
// 定义 AVX512 有符号 32 位整数按位取反操作宏,与无符号 8 位整数取反相同
// 定义 AVX512 无符号 64 位整数按位取反操作宏,与无符号 8 位整数取反相同
// 定义 AVX512 有符号 64 位整数按位取反操作宏,与无符号 8 位整数取反相同
// 如果支持 AVX512DQ,定义 AVX512 单精度浮点数按位取反操作宏,使用 _mm512_xor_ps 实现
// 如果支持 AVX512DQ,定义 AVX512 双精度浮点数按位取反操作宏,使用 _mm512_xor_pd 实现
// 否则,定义 AVX512 单精度浮点数按位取
NPY_FINLINE npyv_b8 npyv_xor_b8(npyv_b8 a, npyv_b8 b)
{ return a ^ b; }
NPY_FINLINE npyv_b16 npyv_xor_b16(npyv_b16 a, npyv_b16 b)
{ return a ^ b; }
NPY_FINLINE npyv_b8 npyv_not_b8(npyv_b8 a)
{ return ~a; }
NPY_FINLINE npyv_b16 npyv_not_b16(npyv_b16 a)
{ return ~a; }
NPY_FINLINE npyv_b8 npyv_andc_b8(npyv_b8 a, npyv_b8 b)
{ return a & (~b); }
NPY_FINLINE npyv_b8 npyv_orc_b8(npyv_b8 a, npyv_b8 b)
{ return a | (~b); }
NPY_FINLINE npyv_b8 npyv_xnor_b8(npyv_b8 a, npyv_b8 b)
{ return ~(a ^ b); }
// 定义 AVX512 下的位与操作宏
// 定义 AVX512 下的位与操作宏
// 定义 AVX512 下的位或操作宏
// 定义 AVX512 下的位或操作宏
// 定义 AVX512 下的位异或操作宏
// 定义 AVX512 下的位异或操作宏
// 定义 AVX512 下的位非操作宏
// 定义 AVX512 下的位非操作宏
// 定义 AVX512 下的位与非操作宏
// 定义 AVX512 下的位或非操作宏
// 定义 AVX512 下的位异或非操作宏
// 定义 AVX512 DQ 指令集下的位与操作宏
// 定义 AVX512 DQ 指令集下的位或操作宏
// 定义 AVX512 DQ 指令集下的位异或操作宏
// 定义 AVX512 DQ 指令集下的位非操作宏
// 如果不支持 AVX512 DQ 指令集,定义 64 位位与操作函数
NPY_FINLINE npyv_b64 npyv_and_b64(npyv_b64 a, npyv_b64 b)
{ return (npyv_b64)_mm512_kand((npyv_b32)a, (npyv_b32)b); }
// 如果不支持 AVX512 DQ 指令集,定义 64 位位或操作函数
NPY_FINLINE npyv_b64 npyv_or_b64(npyv_b64 a, npyv_b64 b)
{ return (npyv_b64)_mm512_kor((npyv_b32)a, (npyv_b32)b); }
// 如果不支持 AVX512 DQ 指令集,定义 64 位位异或操作函数
NPY_FINLINE npyv_b64 npyv_xor_b64(npyv_b64 a, npyv_b64 b)
{ return (npyv_b64)_mm512_kxor((npyv_b32)a, (npyv_b32)b); }
// 如果不支持 AVX512 DQ 指令集,定义 64 位位非操作函数
NPY_FINLINE npyv_b64 npyv_not_b64(npyv_b64 a)
{ return (npyv_b64)_mm512_knot((npyv_b32)a); }
/***************************
* Comparison
***************************/
// int Equal
// 定义 AVX512 BW 指令集下的无符号 8 位相等比较操作宏
// 定义 AVX512 BW 指令集下的有符号 8 位相等比较操作宏
// 定义 AVX512 BW 指令集下的无符号 16 位相等比较操作宏
// 定义 AVX512 BW 指令集下的有符号 16 位相等比较操作宏
// 如果不支持 AVX512 BW 指令集,从 AVX2 转换定义无符号 8 位相等比较操作函数
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_cmpeq_u8, _mm256_cmpeq_epi8)
// 如果不支持 AVX512 BW 指令集,从 AVX2 转换定义无符号 16 位相等比较操作函数
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_cmpeq_u16, _mm256_cmpeq_epi16)
// 如果不支持 AVX512 BW 指令集,定义有符号 8 位相等比较操作宏
// 如果不支持 AVX512 BW 指令集,定义有符号 16 位相等比较操作宏
// 定义 AVX512 下的无符号 32 位相等比较操作宏
// 定义 AVX512 下的有符号 32 位相等比较操作宏
// 定义 AVX512 下的无符号 64 位相等比较操作宏
// 定义 AVX512 下的有符号 64 位相等比较操作宏
// int not equal
// 定义 AVX512 BW 指令集下的无符号 8 位不相等比较操作宏
// 定义 AVX512 BW 指令集下的有符号 8 位不相等比较操作宏
// 定义 AVX512 BW 指令集下的无符号 16 位不相等比较操作宏
// 定义 AVX512 BW 指令集下的有符号 16 位不相等比较操作宏
// 如果不支持 AVX512 BW 指令集,定义无符号 8 位不相等比较操作宏
// 如果不支持 AVX512 BW 指令集,定义无符号 16 位不相等比较操作宏
// 如果不支持 AVX512 BW 指令集,定义有符号 8 位不相等比较操作宏
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_cmpgt_s8, _mm256_cmpgt_epi8)
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv_cmpgt_s16, _mm256_cmpgt_epi16)
NPY_FINLINE __m512i npyv_cmpgt_u8(__m512i a, __m512i b)
{
const __m512i sbit = _mm512_set1_epi32(0x80808080);
return npyv_cmpgt_s8(_mm512_xor_si512(a, sbit), _mm512_xor_si512(b, sbit));
}
NPY_FINLINE __m512i npyv_cmpgt_u16(__m512i a, __m512i b)
{
const __m512i sbit = _mm512_set1_epi32(0x80008000);
return npyv_cmpgt_s16(_mm512_xor_si512(a, sbit), _mm512_xor_si512(b, sbit));
}
// 定义无符号和有符号整数的 AVX-512 指令,用于比较大于的掩码生成
// 如果支持 AVX-512BW 指令集,则定义无符号和有符号整数的大于等于掩码生成指令,否则用大于生成掩码并取反生成大于等于掩码
// 定义无符号和有符号整数的 AVX-512 指令,用于比较大于等于的掩码生成
// 定义无符号和有符号整数的 AVX-512 指令,用于比较小于的掩码生成
// 定义无符号和有符号整数的 AVX-512 指令,用于比较小于等于的掩码生成
// precision comparison
// 定义单精度和双精度浮点数的 AVX-512 指令,用于比较相等、不相等、小于、小于等于、大于、大于等于的掩码生成
// check special cases
NPY_FINLINE npyv_b32 npyv_notnan_f32(npyv_f32 a)
{ return _mm512_cmp_ps_mask(a, a, _CMP_ORD_Q); }
NPY_FINLINE npyv_b64 npyv_notnan_f64(npyv_f64 a)
{ return _mm512_cmp_pd_mask(a, a, _CMP_ORD_Q); }
// 检查特殊情况,返回单精度和双精度浮点数向量中非 NaN 元素的掩码
// Test cross all vector lanes
// any: returns true if any of the elements is not equal to zero
// all: returns true if all elements are not equal to zero
NPY_FINLINE bool npyv_any_
{ return npyv_tobits_
NPY_FINLINE bool npyv_all_
{ return npyv_tobits_
NPYV_IMPL_AVX512_ANYALL(b8, 0xffffffffffffffffull)
NPYV_IMPL_AVX512_ANYALL(b16, 0xfffffffful)
NPYV_IMPL_AVX512_ANYALL(b32, 0xffff)
NPYV_IMPL_AVX512_ANYALL(b64, 0xff)
NPY_FINLINE bool npyv_any_
{ \
return npyv_tobits_
npyv_cmpeq_
) != MASK; \
} \
NPY_FINLINE bool npyv_all_
{ \
return npyv_tobits_
npyv_cmpeq_
) == 0; \
}
NPYV_IMPL_AVX512_ANYALL(u8, b8, 0xffffffffffffffffull)
NPYV_IMPL_AVX512_ANYALL(s8, b8, 0xffffffffffffffffull)
NPYV_IMPL_AVX512_ANYALL(u16, b16, 0xfffffffful)
NPYV_IMPL_AVX512_ANYALL(s16, b16, 0xfffffffful)
NPYV_IMPL_AVX512_ANYALL(u32, b32, 0xffff)
NPYV_IMPL_AVX512_ANYALL(s32, b32, 0xffff)
NPYV_IMPL_AVX512_ANYALL(u64, b64, 0xff)
NPYV_IMPL_AVX512_ANYALL(s64, b64, 0xff)
NPYV_IMPL_AVX512_ANYALL(f32, b32, 0xffff)
NPYV_IMPL_AVX512_ANYALL(f64, b64, 0xff)
.\numpy\numpy\_core\src\common\simd\avx512\reorder.h
// 定义宏:将两个向量的低部分合并
// 定义宏:将两个单精度浮点数向量的低部分合并
_mm512_insertf32x8(A, _mm512_castps512_ps256(B), 1)
// 定义宏:将两个单精度浮点数向量的低部分合并(替代实现)
_mm512_castsi512_ps(npyv_combinel_u8(_mm512_castps_si512(A), _mm512_castps_si512(B)))
// 定义宏:将两个向量的高部分合并
// 定义宏:将两个单精度浮点数向量的高部分合并
_mm512_insertf32x8(B, _mm512_extractf32x8_ps(A, 1), 0)
// 定义宏:将两个单精度浮点数向量的高部分合并(替代实现)
_mm512_castsi512_ps(npyv_combineh_u8(_mm512_castps_si512(A), _mm512_castps_si512(B)))
// 定义函数:从两个整型向量 a 和 b 中组合得到一个 m512ix2 结构体
NPY_FINLINE npyv_m512ix2 npyv__combine(__m512i a, __m512i b)
{
npyv_m512ix2 r;
// 将向量 a 和 b 的低部分合并
r.val[0] = npyv_combinel_u8(a, b);
// 将向量 a 和 b 的高部分合并
r.val[1] = npyv_combineh_u8(a, b);
return r;
}
// 定义函数:从两个单精度浮点数向量 a 和 b 中组合得到一个 f32x2 结构体
NPY_FINLINE npyv_f32x2 npyv_combine_f32(__m512 a, __m512 b)
{
npyv_f32x2 r;
// 将单精度浮点数向量 a 和 b 的低部分合并
r.val[0] = npyv_combinel_f32(a, b);
// 将单精度浮点数向量 a 和 b 的高部分合并
r.val[1] = npyv_combineh_f32(a, b);
return r;
}
// 定义函数:从两个双精度浮点数向量 a 和 b 中组合得到一个 f64x2 结构体
NPY_FINLINE npyv_f64x2 npyv_combine_f64(__m512d a, __m512d b)
{
npyv_f64x2 r;
// 将双精度浮点数向量 a 和 b 的低部分合并
r.val[0] = npyv_combinel_f64(a, b);
// 将双精度浮点数向量 a 和 b 的高部分合并
r.val[1] = npyv_combineh_f64(a, b);
return r;
}
// 定义宏:将两个整型向量的低部分合并,实际调用 npyv__combine 函数
// 定义宏:插入两个向量的低部分,根据 AVX512BW 的可用性选择实现
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv__unpacklo_epi8, _mm256_unpacklo_epi8)
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv__unpackhi_epi8, _mm256_unpackhi_epi8)
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv__unpacklo_epi16, _mm256_unpacklo_epi16)
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv__unpackhi_epi16, _mm256_unpackhi_epi16)
NPY_FINLINE npyv_u64x2 npyv_zip_u64(__m512i a, __m512i b)
{
npyv_u64x2 r;
r.val[0] = _mm512_permutex2var_epi64(a, npyv_set_u64(0, 8, 1, 9, 2, 10, 3, 11), b);
r.val[1] = _mm512_permutex2var_epi64(a, npyv_set_u64(4, 12, 5, 13, 6, 14, 7, 15), b);
return r;
}
NPY_FINLINE npyv_u8x2 npyv_zip_u8(__m512i a, __m512i b)
{
npyv_u8x2 r;
r.val[0] = _mm512_permutex2var_epi8(a,
npyv_set_u8(0, 64, 1, 65, 2, 66, 3, 67, 4, 68, 5, 69, 6, 70, 7, 71,
8, 72, 9, 73, 10, 74, 11, 75, 12, 76, 13, 77, 14, 78, 15, 79,
16, 80, 17, 81, 18, 82, 19, 83, 20, 84, 21, 85, 22, 86, 23, 87,
24, 88, 25, 89, 26, 90, 27, 91, 28, 92, 29, 93, 30, 94, 31, 95), b);
r.val[1] = _mm512_permutex2var_epi8(a,
npyv_set_u8(32, 96, 33, 97, 34, 98, 35, 99, 36, 100, 37, 101, 38, 102, 39, 103,
40, 104, 41, 105, 42, 106, 43, 107, 44, 108, 45, 109, 46, 110, 47, 111,
48, 112, 49, 113, 50, 114, 51, 115, 52, 116, 53, 117, 54, 118, 55, 119,
56, 120, 57, 121, 58, 122, 59, 123, 60, 124, 61, 125, 62, 126, 63, 127), b);
__m512i ab0 = _mm512_unpacklo_epi8(a, b);
__m512i ab1 = _mm512_unpackhi_epi8(a, b);
__m512i ab0 = npyv__unpacklo_epi8(a, b);
__m512i ab1 = npyv__unpackhi_epi8(a, b);
r.val[0] = _mm512_permutex2var_epi64(ab0, npyv_set_u64(0, 1, 8, 9, 2, 3, 10, 11), ab1);
r.val[1] = _mm512_permutex2var_epi64(ab0, npyv_set_u64(4, 5, 12, 13, 6, 7, 14, 15), ab1);
return r;
}
NPY_FINLINE npyv_u16x2 npyv_zip_u16(__m512i a, __m512i b)
{
npyv_u16x2 r;
r.val[0] = _mm512_permutex2var_epi16(a,
npyv_set_u16(0, 32, 1, 33, 2, 34, 3, 35, 4, 36, 5, 37, 6, 38, 7, 39,
8, 40, 9, 41, 10, 42, 11, 43, 12, 44, 13, 45, 14, 46, 15, 47), b);
r.val[1] = _mm512_permutex2var_epi16(a,
npyv_set_u16(16, 48, 17, 49, 18, 50, 19, 51, 20, 52, 21, 53, 22, 54, 23, 55,
24, 56, 25, 57, 26, 58, 27, 59, 28, 60, 29, 61, 30, 62, 31, 63), b);
__m512i ab0 = npyv__unpacklo_epi16(a, b);
__m512i ab1 = npyv__unpackhi_epi16(a, b);
r.val[0] = _mm512_permutex2var_epi64(ab0, npyv_set_u64(0, 1, 8, 9, 2, 3, 10, 11), ab1);
r.val[1] = _mm512_permutex2var_epi64(ab0, npyv_set_u64(4, 5, 12, 13, 6, 7, 14, 15), ab1);
return r;
}
NPY_FINLINE npyv_u32x2 npyv_zip_u32(__m512i a, __m512i b)
{
// 定义一个内联函数,用于将两个 __m512 类型的向量 a 和 b 进行压缩合并成一个 npyv_f32x2 结构体返回
NPY_FINLINE npyv_f32x2 npyv_zip_f32(__m512 a, __m512 b)
{
// 声明一个 npyv_f32x2 结构体 r,用于存储结果
npyv_f32x2 r;
// 使用 _mm512_permutex2var_ps 函数,根据给定的索引将向量 a 和 b 进行混合,结果存入 r.val[0]
r.val[0] = _mm512_permutex2var_ps(a,
npyv_set_u32(0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23), b);
// 同上,将向量 a 和 b 进行混合,结果存入 r.val[1]
r.val[1] = _mm512_permutex2var_ps(a,
npyv_set_u32(8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31), b);
// 返回结果结构体 r
return r;
}
// 定义一个内联函数,用于将两个 __m512d 类型的双精度向量 a 和 b 进行压缩合并成一个 npyv_f64x2 结构体返回
NPY_FINLINE npyv_f64x2 npyv_zip_f64(__m512d a, __m512d b)
{
// 声明一个 npyv_f64x2 结构体 r,用于存储结果
npyv_f64x2 r;
// 使用 _mm512_permutex2var_pd 函数,根据给定的索引将双精度向量 a 和 b 进行混合,结果存入 r.val[0]
r.val[0] = _mm512_permutex2var_pd(a, npyv_set_u64(0, 8, 1, 9, 2, 10, 3, 11), b);
// 同上,将双精度向量 a 和 b 进行混合,结果存入 r.val[1]
r.val[1] = _mm512_permutex2var_pd(a, npyv_set_u64(4, 12, 5, 13, 6, 14, 7, 15), b);
// 返回结果结构体 r
return r;
}
// 定义一个内联函数,用于将两个 npyv_u8 类型的向量 ab0 和 ab1 进行解交错,结果存入 npyv_u8x2 结构体返回
// 如果支持 AVX512VBMI 指令集,则使用 _mm512_permutex2var_epi8 函数进行解交错,否则根据是否支持 AVX512BW 选择相应的处理方式
NPY_FINLINE npyv_u8x2 npyv_unzip_u8(npyv_u8 ab0, npyv_u8 ab1)
{
// 声明一个 npyv_u8x2 结构体 r,用于存储解交错后的结果
npyv_u8x2 r;
// 如果支持 AVX512VBMI,则定义解交错所需的索引 idx_a 和 idx_b
const __m512i idx_a = npyv_set_u8(
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94,
96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126
);
const __m512i idx_b = npyv_set_u8(
1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31,
33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63,
65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95,
97, 99, 101, 103, 105, 107, 109, 111, 113, 115, 117, 119, 121, 123, 125, 127
);
// 使用 _mm512_permutex2var_epi8 函数,根据 idx_a 和 idx_b 进行解交错,结果存入 r.val[0] 和 r.val[1]
r.val[0] = _mm512_permutex2var_epi8(ab0, idx_a, ab1);
r.val[1] = _mm512_permutex2var_epi8(ab0, idx_b, ab1);
// 如果不支持 AVX512VBMI,根据是否支持 AVX512BW 选择相应的处理方式
// 如果支持 AVX512BW,则定义解交错所需的 idx
const __m512i idx = npyv_set_u8(
0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15,
0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15,
0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15,
0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15
);
// 使用 _mm512_shuffle_epi8 函数,根据 idx 进行解交错,结果存入 abl 和 abh
__m512i abl = _mm512_shuffle_epi8(ab0, idx);
__m512i abh = _mm512_shuffle_epi8(ab1, idx);
// 如果以上都不支持,则使用 AVX2 指令集处理
const __m256i idx = _mm256_setr_epi8(
0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15,
0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15
);
// 使用 _mm256_shuffle_epi8 函数,根据 idx 对 ab0 和 ab1 进行解交错,结果存入对应的低位和高位部分
__m256i abl_lo = _mm256_shuffle_epi8(npyv512_lower_si256(ab0), idx);
__m256i abl_hi = _mm256_shuffle_epi
return r;
NPY_FINLINE npyv_u16x2 npyv_unzip_u16(npyv_u16 ab0, npyv_u16 ab1)
{
npyv_u16x2 r;
// 如果支持 AVX-512 BW 指令集,则定义两个索引向量 idx_a 和 idx_b
const __m512i idx_a = npyv_set_u16(
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62
);
const __m512i idx_b = npyv_set_u16(
1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31,
33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63
);
// 使用 permute 操作按照 idx_a 和 idx_b 重新排列输入的 ab0 和 ab1,存储到结果 r 中
r.val[0] = _mm512_permutex2var_epi16(ab0, idx_a, ab1);
r.val[1] = _mm512_permutex2var_epi16(ab0, idx_b, ab1);
// 如果不支持 AVX-512 BW 指令集,则定义一个字节级别的索引向量 idx
const __m256i idx = _mm256_setr_epi8(
0,1, 4,5, 8,9, 12,13, 2,3, 6,7, 10,11, 14,15,
0,1, 4,5, 8,9, 12,13, 2,3, 6,7, 10,11, 14,15
);
// 对输入向量 ab0 和 ab1 进行字节级别的 shuffle 操作,以得到结果 r
__m256i abl_lo = _mm256_shuffle_epi8(npyv512_lower_si256(ab0), idx);
__m256i abl_hi = _mm256_shuffle_epi8(npyv512_higher_si256(ab0), idx);
__m256i abh_lo = _mm256_shuffle_epi8(npyv512_lower_si256(ab1), idx);
__m256i abh_hi = _mm256_shuffle_epi8(npyv512_higher_si256(ab1), idx);
__m512i abl = npyv512_combine_si256(abl_lo, abl_hi);
__m512i abh = npyv512_combine_si256(abh_lo, abh_hi);
// 定义两个 64 位整数型的索引向量 idx_a 和 idx_b
const __m512i idx_a = npyv_set_u64(0, 2, 4, 6, 8, 10, 12, 14);
const __m512i idx_b = npyv_set_u64(1, 3, 5, 7, 9, 11, 13, 15);
// 使用 permute 操作按照 idx_a 和 idx_b 重新排列 abl 和 abh,存储到结果 r 中
r.val[0] = _mm512_permutex2var_epi64(abl, idx_a, abh);
r.val[1] = _mm512_permutex2var_epi64(abl, idx_b, abh);
return r;
}
NPY_FINLINE npyv_u32x2 npyv_unzip_u32(npyv_u32 ab0, npyv_u32 ab1)
{
// 定义两个 32 位整数型的索引向量 idx_a 和 idx_b
const __m512i idx_a = npyv_set_u32(
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30
);
const __m512i idx_b = npyv_set_u32(
1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31
);
npyv_u32x2 r;
// 使用 permute 操作按照 idx_a 和 idx_b 重新排列输入的 ab0 和 ab1,存储到结果 r 中
r.val[0] = _mm512_permutex2var_epi32(ab0, idx_a, ab1);
r.val[1] = _mm512_permutex2var_epi32(ab0, idx_b, ab1);
return r;
}
NPY_FINLINE npyv_u64x2 npyv_unzip_u64(npyv_u64 ab0, npyv_u64 ab1)
{
// 定义两个 64 位整数型的索引向量 idx_a 和 idx_b
const __m512i idx_a = npyv_set_u64(0, 2, 4, 6, 8, 10, 12, 14);
const __m512i idx_b = npyv_set_u64(1, 3, 5, 7, 9, 11, 13, 15);
npyv_u64x2 r;
// 使用 permute 操作按照 idx_a 和 idx_b 重新排列输入的 ab0 和 ab1,存储到结果 r 中
r.val[0] = _mm512_permutex2var_epi64(ab0, idx_a, ab1);
r.val[1] = _mm512_permutex2var_epi64(ab0, idx_b, ab1);
return r;
}
NPY_FINLINE npyv_f32x2 npyv_unzip_f32(npyv_f32 ab0, npyv_f32 ab1)
{
// 定义两个 32 位整数型的索引向量 idx_a 和 idx_b
const __m512i idx_a = npyv_set_u32(
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30
);
const __m512i idx_b = npyv_set_u32(
1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31
);
npyv_f32x2 r;
// 使用 permute 操作按照 idx_a 和 idx_b 重新排列输入的 ab0 和 ab1,存储到结果 r 中
r.val[0] = _mm512_permutex2var_ps(ab0, idx_a, ab1);
r.val[1] = _mm512_permutex2var_ps(ab0, idx_b, ab1);
return r;
}
NPY_FINLINE npyv_f64x2 npyv_unzip_f64(npyv_f64 ab0, npyv_f64 ab1)
{
// 定义两个 64 位整数型的索引向量 idx_a
const __m512i idx_a = npyv_set_u64(0, 2, 4, 6, 8, 10, 12, 14);
npyv_f64x2 r;
// 使用 permute 操作按照 idx_a 重新排列输入的 ab0 和 ab1,存储到结果 r 中
r.val[0] = _mm512_permutex2var_pd(ab0, idx_a, ab1);
return r;
}
// 创建一个包含固定顺序的无符号64位整数的__m512i类型变量,用于索引操作
const __m512i idx_b = npyv_set_u64(1, 3, 5, 7, 9, 11, 13, 15);
// 创建一个双精度浮点数向量变量r,包含两个元素
npyv_f64x2 r;
// 使用_mm512_permutex2var_pd函数对两个双精度浮点数向量ab0和ab1进行按索引混合操作,并将结果存入r的第一个元素
r.val[0] = _mm512_permutex2var_pd(ab0, idx_a, ab1);
// 使用_mm512_permutex2var_pd函数对两个双精度浮点数向量ab0和ab1进行按固定索引顺序(idx_b)混合操作,并将结果存入r的第二个元素
r.val[1] = _mm512_permutex2var_pd(ab0, idx_b, ab1);
// 返回双精度浮点数向量r
return r;
// 如果编译器支持 AVX-512BW 指令集,则使用 AVX-512 实现反转每个 64 位 lane 的操作
const __m512i idx = npyv_set_u8(
// 创建 AVX-512 需要的索引,用于反转每个 64 位 lane 中的元素
7, 6, 5, 4, 3, 2, 1, 0,/*64*/15, 14, 13, 12, 11, 10, 9, 8,
7, 6, 5, 4, 3, 2, 1, 0,/*64*/15, 14, 13, 12, 11, 10, 9, 8,
7, 6, 5, 4, 3, 2, 1, 0,/*64*/15, 14, 13, 12, 11, 10, 9, 8,
7, 6, 5, 4, 3, 2, 1, 0,/*64*/15, 14, 13, 12, 11, 10, 9, 8
);
// 使用 AVX-512 指令集中的 shuffle 操作进行反转操作
return _mm512_shuffle_epi8(a, idx);
// 如果编译器不支持 AVX-512BW 指令集,则使用 AVX2 实现反转每个 64 位 lane 的操作
const __m256i idx = _mm256_setr_epi8(
// 创建 AVX2 需要的索引,用于反转每个 64 位 lane 中的元素
7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8,
7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8
);
// 分别对高低 256 位执行 shuffle 操作,然后将结果合并
__m256i lo = _mm256_shuffle_epi8(npyv512_lower_si256(a), idx);
__m256i hi = _mm256_shuffle_epi8(npyv512_higher_si256(a), idx);
return npyv512_combine_si256(lo, hi);
}
.\numpy\numpy\_core\src\common\simd\avx512\utils.h
// 定义将__m512i类型转换为__m256i类型的宏
// 定义将__m512类型转换为__m256类型的宏
// 定义将__m512d类型转换为__m256d类型的宏
// 定义从__m512i类型中提取高128位__m256i类型的宏
// 定义从__m512d类型中提取高128位__m256d类型的宏
// 如果支持 AVX512DQ,则定义从__m512类型中提取高256位__m256类型的宏
// 如果不支持 AVX512DQ,则通过组合操作从__m512类型中提取高256位__m256类型
_mm256_castsi256_ps(_mm512_extracti64x4_epi64(_mm512_castps_si512(A), 1))
// 定义将两个__m256i类型合并为一个__m512i类型的宏
// 定义将两个__m256d类型合并为一个__m512d类型的宏
// 如果支持 AVX512DQ,则定义将两个__m256类型合并为一个__m512类型的宏
// 如果不支持 AVX512DQ,则通过组合操作将两个__m256类型合并为一个__m512类型
_mm512_castsi512_ps(npyv512_combine_si256(_mm256_castps_si256(A), _mm256_castps_si256(B)))
// 定义宏,用于从AVX2转换到AVX512的单参数函数实现,返回__m512i类型
NPY_FINLINE __m512i FN_NAME(__m512i a) \
{ \
__m256i l_a = npyv512_lower_si256(a); \
__m256i h_a = npyv512_higher_si256(a); \
l_a = INTRIN(l_a); \
h_a = INTRIN(h_a); \
return npyv512_combine_si256(l_a, h_a); \
}
// 定义宏,用于从AVX2转换到AVX512的单参数函数实现,返回__m512类型
NPY_FINLINE __m512 FN_NAME(__m512 a) \
{ \
__m256 l_a = npyv512_lower_ps256(a); \
__m256 h_a = npyv512_higher_ps256(a); \
l_a = INTRIN(l_a); \
h_a = INTRIN(h_a); \
return npyv512_combine_ps256(l_a, h_a); \
}
// 定义宏,用于从AVX2转换到AVX512的单参数函数实现,返回__m512d类型
NPY_FINLINE __m512d FN_NAME(__m512d a) \
{ \
__m256d l_a = npyv512_lower_pd256(a); \
__m256d h_a = npyv512_higher_pd256(a); \
l_a = INTRIN(l_a); \
h_a = INTRIN(h_a); \
return npyv512_combine_pd256(l_a, h_a); \
}
// 定义宏,用于从AVX2转换到AVX512的双参数函数实现,返回__m512i类型
NPY_FINLINE __m512i FN_NAME(__m512i a, __m512i b) \
{
__m256i l_a = npyv512_lower_si256(a);
__m256i h_a = npyv512_higher_si256(a);
__m256i l_b = npyv512_lower_si256(b);
__m256i h_b = npyv512_higher_si256(b);
l_a = INTRIN(l_a, l_b);
h_a = INTRIN(h_a, h_b);
return npyv512_combine_si256(l_a, h_a);
}
// 定义一个内联函数 FN_NAME,接受两个 __m512 类型的参数 a 和 b,返回结果为 __m512 类型
NPY_FINLINE __m512 FN_NAME(__m512 a, __m512 b) \
{ \
// 将 a 和 b 转换为 __m512i 类型,然后调用 INTRIN 进行处理,最后将结果转换回 __m512 类型并返回
return _mm512_castsi512_ps(INTRIN( \
_mm512_castps_si512(a), _mm512_castps_si512(b) \
)); \
}
// 定义一个内联函数 FN_NAME,接受两个 __m512d 类型的参数 a 和 b,返回结果为 __m512d 类型
NPY_FINLINE __m512d FN_NAME(__m512d a, __m512d b) \
{ \
// 将 a 和 b 转换为 __m512i 类型,然后调用 INTRIN 进行处理,最后将结果转换回 __m512d 类型并返回
return _mm512_castsi512_pd(INTRIN( \
_mm512_castpd_si512(a), _mm512_castpd_si512(b) \
)); \
}
// 如果没有 AVX512BW 扩展,则使用 AVX2 的 _mm256_packs_epi16 作为 npyv512_packs_epi16 的定义
NPYV_IMPL_AVX512_FROM_AVX2_2ARG(npyv512_packs_epi16, _mm256_packs_epi16)
// 否则直接定义 npyv512_packs_epi16 为 _mm512_packs_epi16
NPY_FINLINE __m256i npyv512_pack_lo_hi(__m512i a) {
// 提取 a 的低 256 位和高 256 位分别存入 lo 和 hi
__m256i lo = npyv512_lower_si256(a);
__m256i hi = npyv512_higher_si256(a);
// 使用 _mm256_packs_epi32 将 lo 和 hi 中的每对相邻元素进行有符号 16 位整数打包操作,返回结果
return _mm256_packs_epi32(lo, hi);
}
.\numpy\numpy\_core\src\common\simd\emulate_maskop.h
/**
* This header is used internally by all current supported SIMD extensions,
* except for AVX512.
*/
/**
* Implements conditional addition and subtraction.
* e.g. npyv_ifadd_f32(mask, a, b, c) -> mask ? a + b : c
* e.g. npyv_ifsub_f32(mask, a, b, c) -> mask ? a - b : c
*/
NPY_FINLINE npyv_
(npyv_
{ \
npyv_
return npyv_select_
} \
NPY_FINLINE npyv_
(npyv_
{ \
npyv_
return npyv_select_
}
/**
* Conditional addition and subtraction implementation for unsigned and signed 8-bit integers,
* 16-bit integers, 32-bit integers, and 64-bit integers.
*/
NPYV_IMPL_EMULATE_MASK_ADDSUB(u8, b8)
NPYV_IMPL_EMULATE_MASK_ADDSUB(s8, b8)
NPYV_IMPL_EMULATE_MASK_ADDSUB(u16, b16)
NPYV_IMPL_EMULATE_MASK_ADDSUB(s16, b16)
NPYV_IMPL_EMULATE_MASK_ADDSUB(u32, b32)
NPYV_IMPL_EMULATE_MASK_ADDSUB(s32, b32)
NPYV_IMPL_EMULATE_MASK_ADDSUB(u64, b64)
NPYV_IMPL_EMULATE_MASK_ADDSUB(s64, b64)
/**
* Conditional floating-point division implementation.
* e.g. npyv_ifdiv_f32(mask, a, b, c) -> mask ? a / b : c
*/
NPY_FINLINE npyv_f32
npyv_ifdiv_f32(npyv_b32 m, npyv_f32 a, npyv_f32 b, npyv_f32 c)
{
const npyv_f32 one = npyv_setall_f32(1.0f);
npyv_f32 div = npyv_div_f32(a, npyv_select_f32(m, b, one));
return npyv_select_f32(m, div, c);
}
/**
* Conditional floating-point division implementation.
* e.g. npyv_ifdivz_f32(mask, a, b) -> mask ? a / b : 0
*/
NPY_FINLINE npyv_f32
npyv_ifdivz_f32(npyv_b32 m, npyv_f32 a, npyv_f32 b)
{
const npyv_f32 zero = npyv_zero_f32();
return npyv_ifdiv_f32(m, a, b, zero);
}
/**
* Conditional double-precision floating-point division implementation.
* e.g. npyv_ifdiv_f64(mask, a, b, c) -> mask ? a / b : c
*/
NPY_FINLINE npyv_f64
npyv_ifdiv_f64(npyv_b64 m, npyv_f64 a, npyv_f64 b, npyv_f64 c)
{
const npyv_f64 one = npyv_setall_f64(1.0);
npyv_f64 div = npyv_div_f64(a, npyv_select_f64(m, b, one));
return npyv_select_f64(m, div, c);
}
/**
* Conditional double-precision floating-point division implementation.
* e.g. npyv_ifdivz_f64(mask, a, b) -> mask ? a / b : 0
*/
NPY_FINLINE npyv_f64
npyv_ifdivz_f64(npyv_b64 m, npyv_f64 a, npyv_f64 b)
{
const npyv_f64 zero = npyv_zero_f64();
return npyv_ifdiv_f64(m, a, b, zero);
}
注释说明了每个宏定义和条件分支函数的作用,以及它们如何实现条件操作(条件加减和条件除法)的功能。
.\numpy\numpy\_core\src\common\simd\intdiv.h
/**
* This header implements `npyv_divisor_*` intrinsics used for computing the parameters
* of fast integer division, while division intrinsics `npyv_divc_*` are defined in
* {extension}/arithmetic.h.
*/
/**
* bit-scan reverse for non-zeros. returns the index of the highest set bit.
* equivalent to floor(log2(a))
*/
/**
* Inline function to find the index of the highest set bit in a 32-bit unsigned integer.
* Uses compiler-specific intrinsics or assembly for efficient implementation.
*/
NPY_FINLINE unsigned npyv__bitscan_revnz_u32(npy_uint32 a)
{
assert(a > 0); // Ensure input 'a' is non-zero due to the use of __builtin_clz
unsigned long rl;
(void)_BitScanReverse(&rl, (unsigned long)a); // Use _BitScanReverse for MSC compiler
r = (unsigned)rl;
&& (defined(NPY_CPU_X86) || defined(NPY_CPU_AMD64))
__asm__("bsr %1, %0" : "=r" (r) : "r"(a)); // Use inline assembly for GCC, Clang, or Intel Compiler on x86/x86_64
r = 31 - __builtin_clz(a); // Fallback to built-in function __builtin_clz for other architectures
r = 0; // Default to 0 if no specific implementation found
while (a >>= 1) {
r++;
}
return r; // Return the index of the highest set bit
}
/**
* Inline function to find the index of the highest set bit in a 64-bit unsigned integer.
* Uses compiler-specific intrinsics or assembly for efficient implementation.
*/
NPY_FINLINE unsigned npyv__bitscan_revnz_u64(npy_uint64 a)
{
assert(a > 0); // Ensure input 'a' is non-zero due to the use of __builtin_clzll
unsigned long rl;
(void)_BitScanReverse64(&rl, a); // Use _BitScanReverse64 for MSC compiler on AMD64
return (unsigned)rl;
npy_uint64 r;
__asm__("bsrq %1, %0" : "=r"(r) : "r"(a)); // Use inline assembly for GCC, Clang, or Intel Compiler on x86_64
return (unsigned)r;
return 63 - __builtin_clzll(a); // Fallback to built-in function __builtin_clzll for other architectures
npy_uint64 a_hi = a >> 32;
if (a_hi == 0) {
return npyv__bitscan_revnz_u32((npy_uint32)a); // Handle the upper 32 bits if they are zero
}
return 32 + npyv__bitscan_revnz_u32((npy_uint32)a_hi); // Calculate the index of highest set bit for 64-bit 'a'
}
/**
* Inline function to divide a 128-bit unsigned integer by a 64-bit divisor,
* returning the quotient.
*
* This function ensures the divisor is greater than 1.
*/
NPY_FINLINE npy_uint64 npyv__divh128_u64(npy_uint64 high, npy_uint64 divisor)
{
assert(divisor > 1); // Ensure divisor is greater than 1 for valid division
npy_uint64 quotient;
npy_uint64 remainder;
quotient = _udiv128(high, 0, divisor, &remainder); // Use _udiv128 for MSC compiler versions >= 1920
(void)remainder;
__asm__("divq %[d]" : "=a"(quotient) : [d] "r"(divisor), "a"(0), "d"(high)); // Use inline assembly for GCC, Clang, or Intel Compiler on x86_64
quotient = (npy_uint64)((((__uint128_t)high) << 64) / divisor); // Use __uint128_t for 128-bit integers if available
/**
* Minified version based on Donald Knuth’s Algorithm D (Division of nonnegative integers),
* and Generic implementation in Hacker’s Delight.
*
* See https://skanthak.homepage.t-online.de/division.html
* with respect to the license of the Hacker's Delight book
* (https://web.archive.org/web/20190408122508/http://www.hackersdelight.org/permissions.htm)
*/
// 计算使得除数归一化的位移量
unsigned ldz = 63 - npyv__bitscan_revnz_u64(divisor);
// 对除数进行归一化处理
divisor <<= ldz;
high <<= ldz;
// 将除数分解为两个32位的数字
npy_uint32 divisor_hi = divisor >> 32;
npy_uint32 divisor_lo = divisor & 0xFFFFFFFF;
// 计算高位商数数字
npy_uint64 quotient_hi = high / divisor_hi;
npy_uint64 remainder = high - divisor_hi * quotient_hi;
npy_uint64 base32 = 1ULL << 32;
// 使用循环计算更低位的商数数字
while (quotient_hi >= base32 || quotient_hi*divisor_lo > base32*remainder) {
--quotient_hi;
remainder += divisor_hi;
if (remainder >= base32) {
break;
}
}
// 计算被除数的数字对
npy_uint64 dividend_pairs = base32*high - divisor*quotient_hi;
// 计算较低零的第二个商数数字
npy_uint32 quotient_lo = (npy_uint32)(dividend_pairs / divisor_hi);
quotient = base32*quotient_hi + quotient_lo;
#endif
return quotient;
}
// Initializing divisor parameters for unsigned 8-bit division
NPY_FINLINE npyv_u8x3 npyv_divisor_u8(npy_uint8 d)
{
unsigned l, l2, sh1, sh2, m;
switch (d) {
case 0: // LCOV_EXCL_LINE
// 处理可能的除零情况,对于 x86 架构,GCC 插入 `ud2` 指令以替代
// 让硬件/CPU 陷入的行为,这会导致非法指令异常。
// 'volatile' 应该抑制此行为,允许我们引发硬件/CPU 算术异常。
m = sh1 = sh2 = 1 / ((npy_uint8 volatile *)&d)[0];
break;
case 1:
m = 1; sh1 = sh2 = 0;
break;
case 2:
m = 1; sh1 = 1; sh2 = 0;
break;
default:
l = npyv__bitscan_revnz_u32(d - 1) + 1; // 计算 ceil(log2(d))
l2 = (npy_uint8)(1 << l); // 2^l,若 l = 8 则溢出为 0
m = ((npy_uint16)((l2 - d) << 8)) / d + 1; // 计算乘数
sh1 = 1; sh2 = l - 1; // 计算位移量
}
npyv_u8x3 divisor;
#ifdef NPY_HAVE_SSE2 // SSE/AVX2/AVX512
divisor.val[0] = npyv_setall_u16(m);
divisor.val[1] = npyv_set_u8(sh1);
divisor.val[2] = npyv_set_u8(sh2);
#elif defined(NPY_HAVE_VSX2) || defined(NPY_HAVE_VX)
divisor.val[0] = npyv_setall_u8(m);
divisor.val[1] = npyv_setall_u8(sh1);
divisor.val[2] = npyv_setall_u8(sh2);
#elif defined(NPY_HAVE_NEON)
divisor.val[0] = npyv_setall_u8(m);
divisor.val[1] = npyv_reinterpret_u8_s8(npyv_setall_s8(-sh1));
divisor.val[2] = npyv_reinterpret_u8_s8(npyv_setall_s8(-sh2));
#else
#error "please initialize the shifting operand for the new architecture"
#endif
return divisor;
}
// Initializing divisor parameters for signed 8-bit division
NPY_FINLINE npyv_s16x3 npyv_divisor_s16(npy_int16 d);
NPY_FINLINE npyv_s8x3 npyv_divisor_s8(npy_int8 d)
{
#ifdef NPY_HAVE_SSE2 // SSE/AVX2/AVX512
npyv_s16x3 p = npyv_divisor_s16(d);
npyv_s8x3 r;
r.val[0] = npyv_reinterpret_s8_s16(p.val[0]);
r.val[1] = npyv_reinterpret_s8_s16(p.val[1]);
r.val[2] = npyv_reinterpret_s8_s16(p.val[2]);
return r;
#else
int d1 = abs(d);
int sh, m;
if (d1 > 1) {
sh = (int)npyv__bitscan_revnz_u32(d1-1); // 计算 ceil(log2(abs(d))) - 1
m = (1 << (8 + sh)) / d1 + 1; // 计算乘数
}
else if (d1 == 1) {
sh = 0; m = 1;
}
else {
// 对于 d == 0,引发算术异常
sh = m = 1 / ((npy_int8 volatile *)&d)[0]; // LCOV_EXCL_LINE
}
npyv_s8x3 divisor;
divisor.val[0] = npyv_setall_s8(m);
divisor.val[2] = npyv_setall_s8(d < 0 ? -1 : 0);
#if defined(NPY_HAVE_VSX2) || defined(NPY_HAVE_VX)
divisor.val[1] = npyv_setall_s8(sh);
#elif defined(NPY_HAVE_NEON)
divisor.val[1] = npyv_setall_s8(-sh);
#else
#error "please initialize the shifting operand for the new architecture"
#endif
return divisor;
#endif
}
// Initializing divisor parameters for unsigned 16-bit division
NPY_FINLINE npyv_u16x3 npyv_divisor_u16(npy_uint16 d)
{
unsigned l, l2, sh1, sh2, m;
switch (d) {
case 0: // LCOV_EXCL_LINE
// 若 d 等于 0,抛出算术异常
m = sh1 = sh2 = 1 / ((npy_uint16 volatile *)&d)[0];
break;
case 1:
m = 1; sh1 = sh2 = 0;
break;
case 2:
m = 1; sh1 = 1; sh2 = 0;
break;
default:
l = npyv__bitscan_revnz_u32(d - 1) + 1; // 计算 ceil(log2(d))
l2 = (npy_uint16)(1 << l); // 2^l,若 l = 16 则溢出为 0
m = ((l2 - d) << 16) / d + 1; // 计算乘数
sh1 = 1; sh2 = l - 1; // 计算移位数
}
npyv_u16x3 divisor;
divisor.val[0] = npyv_setall_u16(m);
#ifdef NPY_HAVE_SSE2 // SSE/AVX2/AVX512
divisor.val[1] = npyv_set_u16(sh1);
divisor.val[2] = npyv_set_u16(sh2);
#elif defined(NPY_HAVE_VSX2) || defined(NPY_HAVE_VX)
divisor.val[1] = npyv_setall_u16(sh1);
divisor.val[2] = npyv_setall_u16(sh2);
#elif defined(NPY_HAVE_NEON)
divisor.val[1] = npyv_reinterpret_u16_s16(npyv_setall_s16(-sh1));
divisor.val[2] = npyv_reinterpret_u16_s16(npyv_setall_s16(-sh2));
#else
#error "please initialize the shifting operand for the new architecture"
#endif
return divisor;
}
// 为有符号 16 位整数除法初始化除数参数
NPY_FINLINE npyv_s16x3 npyv_divisor_s16(npy_int16 d)
{
int d1 = abs(d);
int sh, m;
if (d1 > 1) {
sh = (int)npyv__bitscan_revnz_u32(d1 - 1); // 计算 ceil(log2(abs(d))) - 1
m = (1 << (16 + sh)) / d1 + 1; // 计算乘数
}
else if (d1 == 1) {
sh = 0; m = 1;
}
else {
// 若 d 等于 0,抛出算术异常
sh = m = 1 / ((npy_int16 volatile *)&d)[0]; // LCOV_EXCL_LINE
}
npyv_s16x3 divisor;
divisor.val[0] = npyv_setall_s16(m);
divisor.val[2] = npyv_setall_s16(d < 0 ? -1 : 0); // 设置除数的符号
#ifdef NPY_HAVE_SSE2 // SSE/AVX2/AVX512
divisor.val[1] = npyv_set_s16(sh);
#elif defined(NPY_HAVE_VSX2) || defined(NPY_HAVE_VX)
divisor.val[1] = npyv_setall_s16(sh);
#elif defined(NPY_HAVE_NEON)
divisor.val[1] = npyv_setall_s16(-sh);
#else
#error "please initialize the shifting operand for the new architecture"
#endif
return divisor;
}
// 为无符号 32 位整数除法初始化除数参数
NPY_FINLINE npyv_u32x3 npyv_divisor_u32(npy_uint32 d)
{
npy_uint32 l, l2, sh1, sh2, m;
switch (d) {
case 0: // LCOV_EXCL_LINE
// 若 d 等于 0,抛出算术异常
m = sh1 = sh2 = 1 / ((npy_uint32 volatile *)&d)[0]; // LCOV_EXCL_LINE
break;
case 1:
m = 1; sh1 = sh2 = 0;
break;
case 2:
m = 1; sh1 = 1; sh2 = 0;
break;
这些注释解释了每个函数和代码段的作用,确保了读者能理解其背后的逻辑和功能。
# 对于默认情况下的处理分支(switch-case结构),计算最小位数l,即ceil(log2(d))
l = npyv__bitscan_revnz_u32(d - 1) + 1; // ceil(log2(d))
# 计算2^l,注意如果l=32可能会导致溢出为0
l2 = (npy_uint32)(1ULL << l); // 2^l, overflow to 0 if l = 32
# 计算乘数m,使用(l2 - d) * 2^32 / d + 1得到的结果
m = ((npy_uint64)(l2 - d) << 32) / d + 1; // multiplier
# 设置sh1为1,sh2为l - 1,这是用于后续的位移操作的位移计数
sh1 = 1; sh2 = l - 1; // shift counts
}
# 创建一个包含三个32位无符号整数的向量,所有元素初始化为m
npyv_u32x3 divisor;
divisor.val[0] = npyv_setall_u32(m);
#ifdef NPY_HAVE_SSE2 // SSE/AVX2/AVX512
// 如果支持 SSE2 或者更高版本的 SIMD 指令集,设置除数的第二和第三元素为 sh1 和 sh2
divisor.val[1] = npyv_set_u32(sh1);
divisor.val[2] = npyv_set_u32(sh2);
#elif defined(NPY_HAVE_VSX2) || defined(NPY_HAVE_VX)
// 如果支持 VSX2 或者 VX 指令集,设置除数的第二和第三元素为 sh1 和 sh2 的全局设定
divisor.val[1] = npyv_setall_u32(sh1);
divisor.val[2] = npyv_setall_u32(sh2);
#elif defined(NPY_HAVE_NEON)
// 如果支持 NEON 指令集,设置除数的第二和第三元素为 -sh1 和 -sh2 的重新解释的无符号整数
divisor.val[1] = npyv_reinterpret_u32_s32(npyv_setall_s32(-sh1));
divisor.val[2] = npyv_reinterpret_u32_s32(npyv_setall_s32(-sh2));
#else
#error "please initialize the shifting operand for the new architecture"
#endif
// 返回初始化后的除数
return divisor;
}
// 初始化有符号 32 位整数除法的除数参数
NPY_FINLINE npyv_s32x3 npyv_divisor_s32(npy_int32 d)
{
npy_int32 d1 = abs(d);
npy_int32 sh, m;
// 处理绝对值溢出的情况
if ((npy_uint32)d == 0x80000000U) {
m = 0x80000001;
sh = 30;
}
else if (d1 > 1) {
// 计算 d1 的对数向上取整减去 1,作为 sh 的值
sh = npyv__bitscan_revnz_u32(d1 - 1); // ceil(log2(abs(d))) - 1
// 计算乘数 m
m = (1ULL << (32 + sh)) / d1 + 1; // multiplier
}
else if (d1 == 1) {
sh = 0; m = 1;
}
else {
// 对于 d == 0,抛出算术异常
sh = m = 1 / ((npy_int32 volatile *)&d)[0]; // LCOV_EXCL_LINE
}
npyv_s32x3 divisor;
// 设置除数的第一个元素为 m
divisor.val[0] = npyv_setall_s32(m);
// 设置除数的第三个元素为 d 的符号
divisor.val[2] = npyv_setall_s32(d < 0 ? -1 : 0); // sign of divisor
#ifdef NPY_HAVE_SSE2 // SSE/AVX2/AVX512
// 如果支持 SSE2 或者更高版本的 SIMD 指令集,设置除数的第二元素为 sh
divisor.val[1] = npyv_set_s32(sh);
#elif defined(NPY_HAVE_VSX2) || defined(NPY_HAVE_VX)
// 如果支持 VSX2 或者 VX 指令集,设置除数的第二元素为 sh 的全局设定
divisor.val[1] = npyv_setall_s32(sh);
#elif defined(NPY_HAVE_NEON)
// 如果支持 NEON 指令集,设置除数的第二元素为 -sh 的全局设定
divisor.val[1] = npyv_setall_s32(-sh);
#else
#error "please initialize the shifting operand for the new architecture"
#endif
// 返回初始化后的除数
return divisor;
}
// 初始化无符号 64 位整数除法的除数参数
NPY_FINLINE npyv_u64x3 npyv_divisor_u64(npy_uint64 d)
{
npyv_u64x3 divisor;
#if defined(NPY_HAVE_VSX2) || defined(NPY_HAVE_VX) || defined(NPY_HAVE_NEON)
// 如果支持 VSX2、VX 或者 NEON 指令集,设置除数的第一个元素为 d 的全局设定
divisor.val[0] = npyv_setall_u64(d);
#else
npy_uint64 l, l2, sh1, sh2, m;
switch (d) {
case 0: // LCOV_EXCL_LINE
// 对于 d == 0,抛出算术异常
m = sh1 = sh2 = 1 / ((npy_uint64 volatile *)&d)[0]; // LCOV_EXCL_LINE
break;
case 1:
m = 1; sh1 = sh2 = 0;
break;
case 2:
m = 1; sh1 = 1; sh2 = 0;
break;
default:
// 计算 d 的对数向上取整加 1,作为 l
l = npyv__bitscan_revnz_u64(d - 1) + 1; // ceil(log2(d))
// 计算 2^l,作为 l2
l2 = l < 64 ? 1ULL << l : 0; // 2^l
// 计算乘数 m
m = npyv__divh128_u64(l2 - d, d) + 1; // multiplier
// 设置移位计数 sh1 和 sh2
sh1 = 1; sh2 = l - 1; // shift counts
}
// 设置除数的第一个元素为 m
divisor.val[0] = npyv_setall_u64(m);
#ifdef NPY_HAVE_SSE2 // SSE/AVX2/AVX512
// 如果支持 SSE2 或者更高版本的 SIMD 指令集,设置除数的第二和第三元素为 sh1 和 sh2
divisor.val[1] = npyv_set_u64(sh1);
divisor.val[2] = npyv_set_u64(sh2);
#else
#error "please initialize the shifting operand for the new architecture"
#endif
#endif
// 返回初始化后的除数
return divisor;
}
// 初始化有符号 64 位整数除法的除数参数
NPY_FINLINE npyv_s64x3 npyv_divisor_s64(npy_int64 d)
{
// 定义一个名为 divisor 的变量,其类型为 npyv_s64x3
npyv_s64x3 divisor;
#if defined(NPY_HAVE_VSX2) || defined(NPY_HAVE_VX) || defined(NPY_HAVE_NEON)
// 设置除数向量的第一个元素为 d 的值
divisor.val[0] = npyv_setall_s64(d);
// 如果 d 是 -1,则设置除数向量的第二个元素为全 1,否则为全 0
divisor.val[1] = npyv_cvt_s64_b64(
npyv_cmpeq_s64(npyv_setall_s64(-1), divisor.val[0])
);
#else
npy_int64 d1 = llabs(d);
npy_int64 sh, m;
// 处理 abs(d) 溢出的情况
if ((npy_uint64)d == 0x8000000000000000ULL) {
m = 0x8000000000000001LL; // 设置特定溢出情况下的修正值
sh = 62; // 对应的位移量
}
else if (d1 > 1) {
sh = npyv__bitscan_revnz_u64(d1 - 1); // 计算 ceil(log2(abs(d))) - 1
m = npyv__divh128_u64(1ULL << sh, d1) + 1; // 计算乘法因子
}
else if (d1 == 1) {
sh = 0; m = 1; // 处理 d = 1 的情况
}
else {
// 对于 d == 0,抛出算术异常
sh = m = 1 / ((npy_int64 volatile *)&d)[0]; // LCOV_EXCL_LINE
// 上面的语句标记为不计算覆盖率,处理 d = 0 的特殊情况
}
// 设置除数向量的第一个元素为 m
divisor.val[0] = npyv_setall_s64(m);
// 设置除数向量的第二个元素为 d 的符号位,如果 d < 0 则为 -1,否则为 0
divisor.val[2] = npyv_setall_s64(d < 0 ? -1 : 0); // 符号位
#ifdef NPY_HAVE_SSE2 // SSE/AVX2/AVX512
divisor.val[1] = npyv_set_s64(sh); // 设置除数向量的第三个元素为 sh
#else
#error "please initialize the shifting operand for the new architecture"
#endif
#endif
// 返回填充好的 divisor 结构体
return divisor;
}
#endif // _NPY_SIMD_INTDIV_H
.\numpy\numpy\_core\src\common\simd\neon\arithmetic.h
// 如果未定义 NPY_SIMD,则抛出错误消息 "Not a standalone header"
// 如果未定义 _NPY_SIMD_NEON_ARITHMETIC_H,则开始定义该头文件
/***************************
* Addition
***************************/
// non-saturated
// 定义各种数据类型的无饱和加法操作
// saturated
// 定义各种数据类型的有饱和加法操作
/***************************
* Subtraction
***************************/
// non-saturated
// 定义各种数据类型的无饱和减法操作
// saturated
// 定义各种数据类型的有饱和减法操作
/***************************
* Multiplication
***************************/
// non-saturated
// 定义各种数据类型的无饱和乘法操作
/***************************
* Integer Division
***************************/
// See simd/intdiv.h for more clarification
// divide each unsigned 8-bit element by a precomputed divisor
// 对每个无符号8位元素进行除法运算,除数为预先计算的值
NPY_FINLINE npyv_u8 npyv_divc_u8(npyv_u8 a, const npyv_u8x3 divisor)
{
const uint8x8_t mulc_lo = vget_low_u8(divisor.val[0]);
// high part of unsigned multiplication
// 无符号乘法的高位部分
uint16x8_t mull_lo = vmull_u8(vget_low_u8(a), mulc_lo);
uint16x8_t mull_hi = vmull_high_u8(a, divisor.val[0]);
// get the high unsigned bytes
// 获取高位的无符号字节
uint8x16_t mulhi = vuzp2q_u8(vreinterpretq_u8_u16(mull_lo), vreinterpretq_u8_u16(mull_hi));
const uint8x8_t mulc_hi = vget_high_u8(divisor.val[0]);
uint16x8_t mull_hi = vmull_u8(vget_high_u8(a), mulc_hi);
uint8x16_t mulhi = vuzpq_u8(vreinterpretq_u8_u16(mull_lo), vreinterpretq_u8_u16(mull_hi)).val[1];
// floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2
// 计算 a/d 的下整值,通过位移和加法
uint8x16_t q = vsubq_u8(a, mulhi);
q = vshlq_u8(q, vreinterpretq_s8_u8(divisor.val[1]));
q = vaddq_u8(mulhi, q);
q = vshlq_u8(q, vreinterpretq_s8_u8(divisor.val[2]));
return q;
}
// divide each signed 8-bit element by a precomputed divisor (round towards zero)
// 对每个有符号8位元素进行除法运算(向零舍入)
NPY_FINLINE npyv_s8 npyv_divc_s8(npyv_s8 a, const npyv_s8x3 divisor)
{
const int8x8_t mulc_lo = vget_low_s8(divisor.val[0]);
// 使用 Neon 指令 vmull_s8 对两个 int8x8_t 类型的寄存器进行有符号乘法运算,得到低位结果
int16x8_t mull_lo = vmull_s8(vget_low_s8(a), mulc_lo);
// 如果定义了 NPY_SIMD_F64 宏,则使用64位SIMD指令进行操作
// 对a和divisor.val[0]的高位进行有符号8位乘法,并获取高16位结果
int16x8_t mull_hi = vmull_high_s8(a, divisor.val[0]);
// 交错打包两个有符号8位向量,从mull_lo和mull_hi中获取高8位
int8x16_t mulhi = vuzp2q_s8(vreinterpretq_s8_s16(mull_lo), vreinterpretq_s8_s16(mull_hi));
// 如果未定义 NPY_SIMD_F64 宏,则执行以下代码块
// 获取divisor.val[0]的高8位
const int8x8_t mulc_hi = vget_high_s8(divisor.val[0]);
// 对a的高8位和mulc_hi进行有符号8位乘法
int16x8_t mull_hi = vmull_s8(vget_high_s8(a), mulc_hi);
// 从mull_lo和mull_hi中交错打包出第二个向量
int8x16_t mulhi = vuzpq_s8(vreinterpretq_s8_s16(mull_lo), vreinterpretq_s8_s16(mull_hi)).val[1];
// q = ((a + mulhi) >> sh1) - XSIGN(a)
// 计算q,将a和mulhi相加后右移sh1位,再减去a的符号位
int8x16_t q = vshlq_s8(vaddq_s8(a, mulhi), divisor.val[1]);
q = vsubq_s8(q, vshrq_n_s8(a, 7));
q = vsubq_s8(veorq_s8(q, divisor.val[2]), divisor.val[2]);
return q;
}
// divide each unsigned 16-bit element by a precomputed divisor
NPY_FINLINE npyv_u16 npyv_divc_u16(npyv_u16 a, const npyv_u16x3 divisor)
{
// 获取divisor.val[0]的低16位
const uint16x4_t mulc_lo = vget_low_u16(divisor.val[0]);
// 对a和mulc_lo进行无符号16位乘法
uint32x4_t mull_lo = vmull_u16(vget_low_u16(a), mulc_lo);
// 如果定义了 NPY_SIMD_F64 宏,则执行以下代码块
// 对a和divisor.val[0]的高16位进行无符号16位乘法
uint32x4_t mull_hi = vmull_high_u16(a, divisor.val[0]);
// 交错打包两个无符号16位向量,从mull_lo和mull_hi中获取高16位
uint16x8_t mulhi = vuzp2q_u16(vreinterpretq_u16_u32(mull_lo), vreinterpretq_u16_u32(mull_hi));
// 如果未定义 NPY_SIMD_F64 宏,则执行以下代码块
// 获取divisor.val[0]的高16位
const uint16x4_t mulc_hi = vget_high_u16(divisor.val[0]);
// 对a的高16位和mulc_hi进行无符号16位乘法
uint32x4_t mull_hi = vmull_u16(vget_high_u16(a), mulc_hi);
// 从mull_lo和mull_hi中交错打包出第二个向量
uint16x8_t mulhi = vuzpq_u16(vreinterpretq_u16_u32(mull_lo), vreinterpretq_u16_u32(mull_hi)).val[1];
// floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2
// 计算floor(a/d),首先计算a-mulhi的右移sh1位后加上mulhi,再左移sh2位
uint16x8_t q = vsubq_u16(a, mulhi);
q = vshlq_u16(q, vreinterpretq_s16_u16(divisor.val[1]));
q = vaddq_u16(mulhi, q);
q = vshlq_u16(q, vreinterpretq_s16_u16(divisor.val[2]));
return q;
}
// divide each signed 16-bit element by a precomputed divisor (round towards zero)
NPY_FINLINE npyv_s16 npyv_divc_s16(npyv_s16 a, const npyv_s16x3 divisor)
{
// 获取divisor.val[0]的低16位
const int16x4_t mulc_lo = vget_low_s16(divisor.val[0]);
// 对a和mulc_lo进行有符号16位乘法
int32x4_t mull_lo = vmull_s16(vget_low_s16(a), mulc_lo);
// 如果定义了 NPY_SIMD_F64 宏,则执行以下代码块
// 对a和divisor.val[0]的高16位进行有符号16位乘法
int32x4_t mull_hi = vmull_high_s16(a, divisor.val[0]);
// 交错打包两个有符号16位向量,从mull_lo和mull_hi中获取高16位
int16x8_t mulhi = vuzp2q_s16(vreinterpretq_s16_s32(mull_lo), vreinterpretq_s16_s32(mull_hi));
// 如果未定义 NPY_SIMD_F64 宏,则执行以下代码块
// 获取divisor.val[0]的高16位
const int16x4_t mulc_hi = vget_high_s16(divisor.val[0]);
// 对a的高16位和mulc_hi进行有符号16位乘法
int32x4_t mull_hi = vmull_s16(vget_high_s16(a), mulc_hi);
// 从mull_lo和mull_hi中交错打包出第二个向量
int16x8_t mulhi = vuzpq_s16(vreinterpretq_s16_s32(mull_lo), vreinterpretq_s16_s32(mull_hi)).val[1];
// q = ((a + mulhi) >> sh1) - XSIGN(a)
// 计算q,将a和mulhi相加后右移sh1位,再减去a的符号位
int16x8_t q = vshlq_s16(vaddq_s16(a, mulhi), divisor.val[1]);
q = vsubq_s16(q, vshrq_n_s16(a, 15));
q = vsubq_s16(veorq_s16(q, divisor.val[2]), divisor.val[2]);
return q;
}
// divide each unsigned 32-bit element by a precomputed divisor
NPY_FINLINE npyv_u32 npyv_divc_u32(npyv_u32 a, const npyv_u32x3 divisor)
{
// 获取除数的低位部分
const uint32x2_t mulc_lo = vget_low_u32(divisor.val[0]);
// 对 a 的低位部分和 mulc_lo 进行无符号乘法,得到64位结果
uint64x2_t mull_lo = vmull_u32(vget_low_u32(a), mulc_lo);
// 对 a 和除数的低位部分进行无符号高位乘法
uint64x2_t mull_hi = vmull_high_u32(a, divisor.val[0]);
// 将乘法结果进行交错排列,得到高位无符号32位结果
uint32x4_t mulhi = vuzp2q_u32(vreinterpretq_u32_u64(mull_lo), vreinterpretq_u32_u64(mull_hi));
// 获取除数的高位部分
const uint32x2_t mulc_hi = vget_high_u32(divisor.val[0]);
// 对 a 的高位部分和 mulc_hi 进行无符号乘法,得到64位结果
uint64x2_t mull_hi = vmull_u32(vget_high_u32(a), mulc_hi);
// 将乘法结果进行交错排列,得到高位无符号32位结果
uint32x4_t mulhi = vuzpq_u32(vreinterpretq_u32_u64(mull_lo), vreinterpretq_u32_u64(mull_hi)).val[1];
// 计算商,使用预先计算的移位因子 divisor.val[1] 和 divisor.val[2]
uint32x4_t q = vsubq_u32(a, mulhi);
q = vshlq_u32(q, vreinterpretq_s32_u32(divisor.val[1]));
q = vaddq_u32(mulhi, q);
q = vshlq_u32(q, vreinterpretq_s32_u32(divisor.val[2]));
return q;
}
// divide each signed 32-bit element by a precomputed divisor (round towards zero)
NPY_FINLINE npyv_s32 npyv_divc_s32(npyv_s32 a, const npyv_s32x3 divisor)
{
// 获取除数的低位部分
const int32x2_t mulc_lo = vget_low_s32(divisor.val[0]);
// 对 a 的低位部分和 mulc_lo 进行有符号乘法,得到64位结果
int64x2_t mull_lo = vmull_s32(vget_low_s32(a), mulc_lo);
// 对 a 和除数的低位部分进行有符号高位乘法
int64x2_t mull_hi = vmull_high_s32(a, divisor.val[0]);
// 将乘法结果进行交错排列,得到高位有符号32位结果
int32x4_t mulhi = vuzp2q_s32(vreinterpretq_s32_s64(mull_lo), vreinterpretq_s32_s64(mull_hi));
// 获取除数的高位部分
const int32x2_t mulc_hi = vget_high_s32(divisor.val[0]);
// 对 a 的高位部分和 mulc_hi 进行有符号乘法,得到64位结果
int64x2_t mull_hi = vmull_s32(vget_high_s32(a), mulc_hi);
// 将乘法结果进行交错排列,得到高位有符号32位结果
int32x4_t mulhi = vuzpq_s32(vreinterpretq_s32_s64(mull_lo), vreinterpretq_s32_s64(mull_hi)).val[1];
// 计算商,使用预先计算的移位因子 divisor.val[1] 和 divisor.val[2]
int32x4_t q = vshlq_s32(vaddq_s32(a, mulhi), divisor.val[1]);
q = vsubq_s32(q, vshrq_n_s32(a, 31));
q = vsubq_s32(veorq_s32(q, divisor.val[2]), divisor.val[2]);
return q;
}
// divide each unsigned 64-bit element by a divisor
NPY_FINLINE npyv_u64 npyv_divc_u64(npyv_u64 a, const npyv_u64x3 divisor)
{
// 获取除数的第一个元素作为64位整数
const uint64_t d = vgetq_lane_u64(divisor.val[0], 0);
// 将 a 的每个64位元素除以 d,返回结果
return npyv_set_u64(vgetq_lane_u64(a, 0) / d, vgetq_lane_u64(a, 1) / d);
}
// returns the high 64 bits of signed 64-bit multiplication
NPY_FINLINE npyv_s64 npyv_divc_s64(npyv_s64 a, const npyv_s64x3 divisor)
{
// 获取除数的第一个元素作为64位整数
const int64_t d = vgetq_lane_s64(divisor.val[0], 0);
// 将 a 的每个64位元素除以 d,返回结果
return npyv_set_s64(vgetq_lane_s64(a, 0) / d, vgetq_lane_s64(a, 1) / d);
}
/***************************
* Division
***************************/
// 如果定义了 NPY_SIMD_F64,则使用内置的单精度浮点除法
// 否则定义一个函数 npyv_div_f32,实现单精度浮点除法
NPY_FINLINE npyv_f32 npyv_div_f32(npyv_f32 a, npyv_f32 b)
{
// 基于 ARM 文档,参见 https://developer.arm.com/documentation/dui0204/j/CIHDIACI
// 估算 b 的倒数
npyv_f32 recipe = vrecpeq_f32(b);
/**
* 牛顿-拉弗森迭代法:
* x[n+1] = x[n] * (2 - d * x[n])
* 当 x0 是应用于 d 的 VRECPE 结果时,收敛到 (1/d)。
*
* 注意:至少需要 3 次迭代以提高精度。
*/
recipe = vmulq_f32(vrecpsq_f32(b, recipe), recipe);
recipe = vmulq_f32(vrecpsq_f32(b, recipe), recipe);
recipe = vmulq_f32(vrecpsq_f32(b, recipe), recipe);
// 返回 a/b = a * recip(b) 的结果
return vmulq_f32(a, recipe);
}
// multiply and add, a*b + c
NPY_FINLINE npyv_f32 npyv_muladd_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c)
{ return vfmaq_f32(c, a, b); }
// multiply and subtract, a*b - c
NPY_FINLINE npyv_f32 npyv_mulsub_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c)
{ return vfmaq_f32(vnegq_f32(c), a, b); }
// negate multiply and add, -(a*b) + c
NPY_FINLINE npyv_f32 npyv_nmuladd_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c)
{ return vfmsq_f32(c, a, b); }
// negate multiply and subtract, -(a*b) - c
NPY_FINLINE npyv_f32 npyv_nmulsub_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c)
{ return vfmsq_f32(vnegq_f32(c), a, b); }
// multiply and add, a*b + c
NPY_FINLINE npyv_f32 npyv_muladd_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c)
{ return vmlaq_f32(c, a, b); }
// multiply and subtract, a*b - c
NPY_FINLINE npyv_f32 npyv_mulsub_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c)
{ return vmlaq_f32(vnegq_f32(c), a, b); }
// negate multiply and add, -(a*b) + c
NPY_FINLINE npyv_f32 npyv_nmuladd_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c)
{ return vmlsq_f32(c, a, b); }
// negate multiply and subtract, -(a*b) - c
NPY_FINLINE npyv_f32 npyv_nmulsub_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c)
{ return vmlsq_f32(vnegq_f32(c), a, b); }
// multiply, add for odd elements and subtract even elements.
// (a * b) -+ c
NPY_FINLINE npyv_f32 npyv_muladdsub_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c)
{
// Create a mask for selecting odd and even elements
const npyv_f32 msign = npyv_set_f32(-0.0f, 0.0f, -0.0f, 0.0f);
// XOR operation to toggle the sign bit, achieving -(c)
// Perform fused multiply-add or fused multiply-subtract based on the mask
return npyv_muladd_f32(a, b, npyv_xor_f32(msign, c));
}
// F64 versions of fused operations for systems supporting SIMD with 64-bit floats
NPY_FINLINE npyv_f64 npyv_muladd_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c)
{ return vfmaq_f64(c, a, b); }
NPY_FINLINE npyv_f64 npyv_mulsub_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c)
{ return vfmaq_f64(vnegq_f64(c), a, b); }
NPY_FINLINE npyv_f64 npyv_nmuladd_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c)
{ return vfmsq_f64(c, a, b); }
NPY_FINLINE npyv_f64 npyv_nmulsub_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c)
{ return vfmsq_f64(vnegq_f64(c), a, b); }
// Multiply, add for odd elements and subtract even elements for F64
NPY_FINLINE npyv_f64 npyv_muladdsub_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c)
{
// Mask for selecting odd and even elements
const npyv_f64 msign = npyv_set_f64(-0.0, 0.0);
// XOR operation to toggle the sign bit, achieving -(c)
// Perform fused multiply-add or fused multiply-subtract based on the mask
return npyv_muladd_f64(a, b, npyv_xor_f64(msign, c));
}
// Reduce sum across vector
// SIMD operations for floating point 64-bit sums
// Summation for 64-bit unsigned integers
NPY_FINLINE npy_uint64 npyv_sum_u64(npyv_u64 a)
{
// Extract and sum low and high parts of the vector
return vget_lane_u64(vadd_u64(vget_low_u64(a), vget_high_u64(a)), 0);
}
// Summation for 32-bit unsigned integers
NPY_FINLINE npy_uint32 npyv_sum_u32(npyv_u32 a)
// 使用 NEON 指令,将输入向量 a 的低32位和高32位分别相加,生成新的向量 a0
uint32x2_t a0 = vpadd_u32(vget_low_u32(a), vget_high_u32(a));
// 使用 NEON 指令,将向量 a0 和输入向量 a 的高32位分别相加,返回相加结果的第0个元素,转换为无符号整数并返回
return (unsigned)vget_lane_u32(vpadd_u32(a0, vget_high_u32(a)), 0);
}
// 定义一个内联函数,计算给定的浮点型 NEON 向量 a 中所有元素的总和
NPY_FINLINE float npyv_sum_f32(npyv_f32 a)
{
// 使用 NEON 指令,将向量 a 中的高32位和低32位分别相加,得到结果向量 r
float32x2_t r = vadd_f32(vget_high_f32(a), vget_low_f32(a));
// 使用 NEON 指令,将结果向量 r 和自身相加,再取结果向量的第0个元素作为返回值,即向量中所有元素的总和
return vget_lane_f32(vpadd_f32(r, r), 0);
}
// 如果定义了 NPY_SIMD_F64 宏,则使用 vaddlvq_u8 和 vaddlvq_u16 宏来进行向量内部求和操作
// 如果未定义 NPY_SIMD_F64 宏,则定义以下两个函数进行向量内部求和操作
// 对无符号8位整数向量进行求和,返回一个16位无符号整数
NPY_FINLINE npy_uint16 npyv_sumup_u8(npyv_u8 a)
{
uint32x4_t t0 = vpaddlq_u16(vpaddlq_u8(a)); // 将每个8位整数加倍扩展到16位,然后两两相加
uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); // 横向求和16位结果的低32位和高32位
return vget_lane_u32(vpadd_u32(t1, t1), 0); // 将低32位和高32位再次相加并返回最低32位结果
}
// 对无符号16位整数向量进行求和,返回一个32位无符号整数
NPY_FINLINE npy_uint32 npyv_sumup_u16(npyv_u16 a)
{
uint32x4_t t0 = vpaddlq_u16(a); // 将每个16位整数向量两两相加
uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); // 横向求和16位结果的低32位和高32位
return vget_lane_u32(vpadd_u32(t1, t1), 0); // 将低32位和高32位再次相加并返回最低32位结果
}