NumPy 源码解析(五十六)
.\numpy\numpy\_core\src\common\simd\neon\conversion.h
// convert boolean vectors to integer vectors
// reinterpret unsigned 8-bit vector as signed 8-bit vector
// reinterpret unsigned 16-bit vector as signed 16-bit vector
// reinterpret unsigned 32-bit vector as signed 32-bit vector
// reinterpret unsigned 64-bit vector as signed 64-bit vector
// reinterpret unsigned 32-bit vector as float 32-bit vector
// reinterpret unsigned 64-bit vector as float 64-bit vector
// convert integer vectors to boolean vectors
// reinterpret unsigned 8-bit vector as signed 8-bit vector
// reinterpret unsigned 16-bit vector as signed 16-bit vector
// reinterpret unsigned 32-bit vector as signed 32-bit vector
// reinterpret unsigned 64-bit vector as signed 64-bit vector
// reinterpret unsigned 32-bit vector as float 32-bit vector
// reinterpret unsigned 64-bit vector as float 64-bit vector
// convert boolean vector to integer bitfield
NPY_FINLINE npy_uint64 npyv_tobits_b8(npyv_b8 a)
{
// Define scaling factors for bits
const npyv_u8 scale = npyv_set_u8(1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128);
// Apply bitwise AND to extract scaled bits
npyv_u8 seq_scale = vandq_u8(a, scale);
// Define byteOrder for AArch64 architecture
const npyv_u8 byteOrder = {0,8,1,9,2,10,3,11,4,12,5,13,6,14,7,15};
// Rearrange seq_scale according to byteOrder
npyv_u8 v0 = vqtbl1q_u8(seq_scale, byteOrder);
// Sum the values horizontally and return as 64-bit unsigned integer
return vaddlvq_u16(vreinterpretq_u16_u8(v0));
// Sum seq_scale values horizontally and return as 64-bit unsigned integer
npyv_u64 sumh = vpaddlq_u32(vpaddlq_u16(vpaddlq_u8(seq_scale)));
return vgetq_lane_u64(sumh, 0) + ((int)vgetq_lane_u64(sumh, 1) << 8);
}
NPY_FINLINE npy_uint64 npyv_tobits_b16(npyv_b16 a)
{
// Define scaling factors for bits
const npyv_u16 scale = npyv_set_u16(1, 2, 4, 8, 16, 32, 64, 128);
// Apply bitwise AND to extract scaled bits
npyv_u16 seq_scale = vandq_u16(a, scale);
// Sum seq_scale values and return as 64-bit unsigned integer
return vaddvq_u16(seq_scale);
// Sum seq_scale values horizontally and return as 64-bit unsigned integer
npyv_u64 sumh = vpaddlq_u32(vpaddlq_u16(seq_scale));
return vgetq_lane_u64(sumh, 0) + vgetq_lane_u64(sumh, 1);
}
NPY_FINLINE npy_uint64 npyv_tobits_b32(npyv_b32 a)
{
// Define scaling factors for bits
const npyv_u32 scale = npyv_set_u32(1, 2, 4, 8);
// Apply bitwise AND to extract scaled bits
npyv_u32 seq_scale = vandq_u32(a, scale);
// Sum seq_scale values and return as 64-bit unsigned integer
return vaddvq_u32(seq_scale);
// Sum seq_scale values horizontally and return as 64-bit unsigned integer
npyv_u64 sumh = vpaddlq_u32(seq_scale);
return vgetq_lane_u64(sumh, 0) + vgetq_lane_u64(sumh, 1);
}
NPY_FINLINE npy_uint64 npyv_tobits_b64(npyv_b64 a)
{
// Extract and combine bits from a 64-bit vector into a 64-bit integer
uint64_t lo = vgetq_lane_u64(a, 0);
uint64_t hi = vgetq_lane_u64(a, 1);
return ((hi & 0x2) | (lo & 0x1));
}
//expand
// Expand 8-bit unsigned integers to 16-bit unsigned integers
NPY_FINLINE npyv_u16x2 npyv_expand_u16_u8(npyv_u8 data) {
npyv_u16x2 r;
// Expand lower 8 bytes to 16 bytes
r.val[0] = vmovl_u8(vget_low_u8(data));
// Expand higher 8 bytes to 16 bytes
r.val[1] = vmovl_u8(vget_high_u8(data));
return r;
}
// Expand 16-bit unsigned integers to 32-bit unsigned integers
NPY_FINLINE npyv_u32x2 npyv_expand_u32_u16(npyv_u16 data) {
npyv_u32x2 r;
// Expand lower 16 bytes to 32 bytes
r.val[0] = vmovl_u16(vget_low_u16(data));
// Expand higher 16 bytes to 32 bytes
r.val[1] = vmovl_u16(vget_high_u16(data));
return r;
}
// pack two 16-bit boolean into one 8-bit boolean vector
NPY_FINLINE npyv_b8 npyv_pack_b8_b16(npyv_b16 a, npyv_b16 b) {
// 将参数 a 和 b 转换为 uint8x16_t 类型的向量,并执行无符号 8 位整数对齐加密(vuzp1q_u8)操作,
// 返回结果向量。
return vuzp1q_u8((uint8x16_t)a, (uint8x16_t)b);
// pack four 32-bit boolean vectors into one 8-bit boolean vector
NPY_FINLINE npyv_b8
npyv_pack_b8_b32(npyv_b32 a, npyv_b32 b, npyv_b32 c, npyv_b32 d) {
// Interleave the lower halves of vectors a and b, and c and d
npyv_b16 ab = vuzp1q_u16((uint16x8_t)a, (uint16x8_t)b);
npyv_b16 cd = vuzp1q_u16((uint16x8_t)c, (uint16x8_t)d);
// Combine and narrow the 32-bit vectors a, b, c, and d to 16-bit vectors ab and cd
npyv_b16 ab = vcombine_u16(vmovn_u32(a), vmovn_u32(b));
npyv_b16 cd = vcombine_u16(vmovn_u32(c), vmovn_u32(d));
// Pack the 16-bit boolean vectors ab and cd into one 8-bit boolean vector
return npyv_pack_b8_b16(ab, cd);
}
// pack eight 64-bit boolean vectors into one 8-bit boolean vector
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) {
// Interleave the lower halves of vectors a, b, c, d, e, f, g, and h
npyv_b32 ab = vuzp1q_u32((uint32x4_t)a, (uint32x4_t)b);
npyv_b32 cd = vuzp1q_u32((uint32x4_t)c, (uint32x4_t)d);
npyv_b32 ef = vuzp1q_u32((uint32x4_t)e, (uint32x4_t)f);
npyv_u32 gh = vuzp1q_u32((uint32x4_t)g, (uint32x4_t)h);
// Combine and narrow the 64-bit vectors a, b, c, d, e, f, g, and h to 32-bit vectors ab, cd, ef, and gh
npyv_b32 ab = vcombine_u32(vmovn_u64(a), vmovn_u64(b));
npyv_b32 cd = vcombine_u32(vmovn_u64(c), vmovn_u64(d));
npyv_b32 ef = vcombine_u32(vmovn_u64(e), vmovn_u64(f));
npyv_b32 gh = vcombine_u32(vmovn_u64(g), vmovn_u64(h));
// Pack the 32-bit boolean vectors ab, cd, ef, and gh into one 8-bit boolean vector
return npyv_pack_b8_b32(ab, cd, ef, gh);
}
// round to nearest integer
// Round each element of vectors a and b to the nearest 32-bit signed integer
NPY_FINLINE npyv_s32 npyv_round_s32_f64(npyv_f64 a, npyv_f64 b)
{
npyv_s64 lo = vcvtnq_s64_f64(a), hi = vcvtnq_s64_f64(b);
// Combine the lower halves of vectors lo and hi into a 32-bit vector
return vcombine_s32(vmovn_s64(lo), vmovn_s64(hi));
}
// Round each element of vector a to the nearest 32-bit signed integer
NPY_FINLINE npyv_s32 npyv_round_s32_f32(npyv_f32 a)
{
// Set the sign bit mask to 0x80000000
const npyv_u32 sign = vdupq_n_u32(0x80000000);
// Set the half value to 0.5
const npyv_f32 half = vdupq_n_f32(0.5f);
// Conditionally select the sign bit or half depending on the sign of a
npyv_f32 sign_half = vbslq_f32(sign, a, half);
// Round each element of vector a to the nearest 32-bit signed integer
return vcvtq_s32_f32(vaddq_f32(a, sign_half));
}
.\numpy\numpy\_core\src\common\simd\neon\math.h
/***************************
* Elementary
***************************/
// Absolute
// Square
// 定义计算单精度浮点数向量平方的函数
NPY_FINLINE npyv_f32 npyv_square_f32(npyv_f32 a)
{ return vmulq_f32(a, a); }
// 定义计算双精度浮点数向量平方的函数
NPY_FINLINE npyv_f64 npyv_square_f64(npyv_f64 a)
{ return vmulq_f64(a, a); }
// Square root
// 对双精度浮点数向量进行平方根计算
// 基于 ARM 文档,参考 https://developer.arm.com/documentation/dui0204/j/CIHDIACI
// 定义计算单精度浮点数向量平方根的函数
NPY_FINLINE npyv_f32 npyv_sqrt_f32(npyv_f32 a)
{
// 定义常量
const npyv_f32 zero = vdupq_n_f32(0.0f);
const npyv_u32 pinf = vdupq_n_u32(0x7f800000);
// 检查是否为零或无穷大
npyv_u32 is_zero = vceqq_f32(a, zero), is_inf = vceqq_u32(vreinterpretq_u32_f32(a), pinf);
// 防止浮点数除零错误
npyv_f32 guard_byz = vbslq_f32(is_zero, vreinterpretq_f32_u32(pinf), a);
// 估算 (1/√a)
npyv_f32 rsqrte = vrsqrteq_f32(guard_byz);
/**
* 牛顿-拉弗森迭代法:
* x[n+1] = x[n] * (3-d * (x[n]*x[n]) )/2)
* 当 x0 是应用于 d 的 VRSQRTE 的结果时,收敛到 (1/√d)。
*
* 注意:至少需要 3 次迭代以提高精度
*/
rsqrte = vmulq_f32(vrsqrtsq_f32(vmulq_f32(a, rsqrte), rsqrte), rsqrte);
rsqrte = vmulq_f32(vrsqrtsq_f32(vmulq_f32(a, rsqrte), rsqrte), rsqrte);
rsqrte = vmulq_f32(vrsqrtsq_f32(vmulq_f32(a, rsqrte), rsqrte), rsqrte);
// a * (1/√a)
npyv_f32 sqrt = vmulq_f32(a, rsqrte);
// 如果 a 是零,则返回零;如果 a 是正无穷大,则返回正无穷大
return vbslq_f32(vorrq_u32(is_zero, is_inf), a, sqrt);
}
// Reciprocal
// 定义计算单精度浮点数向量倒数的函数
NPY_FINLINE npyv_f32 npyv_recip_f32(npyv_f32 a)
{
// 定义计算双精度浮点数向量倒数的函数
const npyv_f32 one = vdupq_n_f32(1.0f);
return npyv_div_f32(one, a);
// 使用 VRECPE 应用于 d 的结果 x0,收敛到 (1/d) 的牛顿-拉弗森迭代法:
npyv_f32 recipe = vrecpeq_f32(a);
recipe = vmulq_f32(vrecpsq_f32(a, recipe), recipe);
recipe = vmulq_f32(vrecpsq_f32(a, recipe), recipe);
recipe = vmulq_f32(vrecpsq_f32(a, recipe), recipe);
return recipe;
}
// 定义计算双精度浮点数向量倒数的函数
NPY_FINLINE npyv_f64 npyv_recip_f64(npyv_f64 a)
{
const npyv_f64 one = vdupq_n_f64(1.0);
return npyv_div_f64(one, a);
}
// Maximum, natively mapping with no guarantees to handle NaN.
// 定义计算单精度浮点数向量最大值的宏,无法保证处理 NaN
// Maximum, supports IEEE floating-point arithmetic (IEC 60559),
// 支持 IEEE 浮点数算术(IEC 60559)
// 如果使用 ASIMD 指令集,定义 npyv_maxp_f32 为 vmaxnmq_f32 函数
// 如果未使用 ASIMD 指令集,定义 npyv_maxp_f32 函数
NPY_FINLINE npyv_f32 npyv_maxp_f32(npyv_f32 a, npyv_f32 b)
{
// 使用 vceqq_f32 函数比较 a 是否为 NaN,结果存储在 nn_a 中
npyv_u32 nn_a = vceqq_f32(a, a);
// 使用 vceqq_f32 函数比较 b 是否为 NaN,结果存储在 nn_b 中
npyv_u32 nn_b = vceqq_f32(b, b);
// 返回根据 nn_a 和 nn_b 条件选择的最大值向量
return vmaxq_f32(vbslq_f32(nn_a, a, b), vbslq_f32(nn_b, b, a));
}
// 定义 npyv_maxn_f32 函数为 vmaxq_f32 函数
// 最大化函数,传播 NaN
// 如果任意对应的元素是 NaN,则设置 NaN
// 如果支持双精度 SIMD 计算
// 定义 npyv_maxp_f64 函数为 vmaxnmq_f64 函数
// 定义 npyv_maxn_f64 函数为 vmaxq_f64 函数
// 最大化函数,整数操作
// 定义 npyv_max_u64 函数
// 返回 a 和 b 中每个元素的最大值
NPY_FINLINE npyv_u64 npyv_max_u64(npyv_u64 a, npyv_u64 b)
{
// 使用 vbslq_u64 函数根据 npyv_cmpgt_u64(a, b) 的结果选择 a 或 b 中的元素
return vbslq_u64(npyv_cmpgt_u64(a, b), a, b);
}
// 定义 npyv_max_s64 函数
// 返回 a 和 b 中每个元素的最大值
NPY_FINLINE npyv_s64 npyv_max_s64(npyv_s64 a, npyv_s64 b)
{
// 使用 vbslq_s64 函数根据 npyv_cmpgt_s64(a, b) 的结果选择 a 或 b 中的元素
return vbslq_s64(npyv_cmpgt_s64(a, b), a, b);
}
// 最小化函数,本地映射,不保证处理 NaN
// 最小化函数,支持 IEEE 浮点运算 (IEC 60559)
// - 如果两个向量中的一个包含 NaN,则设置另一个向量中相应的元素
// - 只有当两个对应元素都是 NaN 时,才设置 NaN
// 如果使用 ASIMD 指令集,定义 npyv_minp_f32 为 vminnmq_f32 函数
// 如果未使用 ASIMD 指令集,定义 npyv_minp_f32 函数
NPY_FINLINE npyv_f32 npyv_minp_f32(npyv_f32 a, npyv_f32 b)
{
// 使用 vceqq_f32 函数比较 a 是否为 NaN,结果存储在 nn_a 中
npyv_u32 nn_a = vceqq_f32(a, a);
// 使用 vceqq_f32 函数比较 b 是否为 NaN,结果存储在 nn_b 中
npyv_u32 nn_b = vceqq_f32(b, b);
// 返回根据 nn_a 和 nn_b 条件选择的最小值向量
return vminq_f32(vbslq_f32(nn_a, a, b), vbslq_f32(nn_b, b, a));
}
// 定义 npyv_minn_f32 函数为 vminq_f32 函数
// 最小化函数,传播 NaN
// 如果任意对应的元素是 NaN,则设置 NaN
// 如果支持双精度 SIMD 计算
// 定义 npyv_minp_f64 函数为 vminnmq_f64 函数
// 定义 npyv_minn_f64 函数为 vminq_f64 函数
// 最小化函数,整数操作
// 定义 npyv_min_u64 函数
// 返回 a 和 b 中每个元素的最小值
NPY_FINLINE npyv_u64 npyv_min_u64(npyv_u64 a, npyv_u64 b)
{
// 使用 vbslq_u64 函数根据 npyv_cmplt_u64(a, b) 的结果选择 a 或 b 中的元素
return vbslq_u64(npyv_cmplt_u64(a, b), a, b);
}
// 定义 npyv_min_s64 函数
// 返回 a 和 b 中每个元素的最小值
NPY_FINLINE npyv_s64 npyv_min_s64(npyv_s64 a, npyv_s64 b)
{
// 使用 vbslq_s64 函数根据 npyv_cmplt_s64(a, b) 的结果选择 a 或 b 中的元素
return vbslq_s64(npyv_cmplt_s64(a, b), a, b);
}
// 减少所有数据类型的最小/最大值
// 如果支持双精度 SIMD 计算
定义了一个宏 `npyv_reduce_maxp_f64`,用于表示将向量中的浮点数类型的元素进行最大值约简。
定义了一个宏 `npyv_reduce_min_u8`,用于表示将向量中的无符号8位整数类型的元素进行最小值约简。
定义了一个宏 `npyv_reduce_min_s8`,用于表示将向量中的有符号8位整数类型的元素进行最小值约简。
定义了一个宏 `npyv_reduce_min_u16`,用于表示将向量中的无符号16位整数类型的元素进行最小值约简。
定义了一个宏 `npyv_reduce_min_s16`,用于表示将向量中的有符号16位整数类型的元素进行最小值约简。
定义了一个宏 `npyv_reduce_min_u32`,用于表示将向量中的无符号32位整数类型的元素进行最小值约简。
定义了一个宏 `npyv_reduce_min_s32`,用于表示将向量中的有符号32位整数类型的元素进行最小值约简。
定义了一个宏 `npyv_reduce_min_f32`,用于表示将向量中的单精度浮点数类型的元素进行最小值约简。
定义了一个宏 `npyv_reduce_min_f64`,用于表示将向量中的双精度浮点数类型的元素进行最小值约简。
定义了一个宏 `npyv_reduce_minn_f32`,用于表示将向量中的单精度浮点数类型的元素进行最小值约简。
定义了一个宏 `npyv_reduce_minn_f64`,用于表示将向量中的双精度浮点数类型的元素进行最小值约简。
定义了一个宏 `npyv_reduce_minp_f32`,用于表示将向量中的单精度浮点数类型的元素进行带负最小值约简。
定义了一个宏 `npyv_reduce_minp_f64`,用于表示将向量中的双精度浮点数类型的元素进行带负最小值约简。
// 定义 NEON 求最小值和最大值的宏函数
// 实现 NEON 向量的最小值或最大值求解函数
NPY_FINLINE npy_
{ \
// 使用 NEON 指令求取向量的最小值或最大值
STYPE
r = vp
r = vp
r = vp
// 返回结果向量中的第一个元素作为标量结果
return (npy_
}
// 实例化 uint8 类型的最小值和最大值求解函数
NPY_IMPL_NEON_REDUCE_MINMAX(min, uint8, u8)
NPY_IMPL_NEON_REDUCE_MINMAX(max, uint8, u8)
// 实例化 int8 类型的最小值和最大值求解函数
NPY_IMPL_NEON_REDUCE_MINMAX(min, int8, s8)
NPY_IMPL_NEON_REDUCE_MINMAX(max, int8, s8)
// 取消宏定义 NPY_IMPL_NEON_REDUCE_MINMAX
// 定义 NEON 求最小值和最大值的宏函数
// 实现 NEON 向量的最小值或最大值求解函数
NPY_FINLINE npy_
{ \
// 使用 NEON 指令求取向量的最小值或最大值
STYPE
r = vp
r = vp
// 返回结果向量中的第一个元素作为标量结果
return (npy_
}
// 实例化 uint16 类型的最小值和最大值求解函数
NPY_IMPL_NEON_REDUCE_MINMAX(min, uint16, u16)
NPY_IMPL_NEON_REDUCE_MINMAX(max, uint16, u16)
// 实例化 int16 类型的最小值和最大值求解函数
NPY_IMPL_NEON_REDUCE_MINMAX(min, int16, s16)
NPY_IMPL_NEON_REDUCE_MINMAX(max, int16, s16)
// 取消宏定义 NPY_IMPL_NEON_REDUCE_MINMAX
// 定义 NEON 求最小值和最大值的宏函数
// 实现 NEON 向量的最小值或最大值求解函数
NPY_FINLINE npy_
{ \
// 使用 NEON 指令求取向量的最小值或最大值
STYPE
r = vp
// 返回结果向量中的第一个元素作为标量结果
return (npy_
}
// 实例化 uint32 类型的最小值和最大值求解函数
NPY_IMPL_NEON_REDUCE_MINMAX(min, uint32, u32)
NPY_IMPL_NEON_REDUCE_MINMAX(max, uint32, u32)
// 实例化 int32 类型的最小值和最大值求解函数
NPY_IMPL_NEON_REDUCE_MINMAX(min, int32, s32)
NPY_IMPL_NEON_REDUCE_MINMAX(max, int32, s32)
// 取消宏定义 NPY_IMPL_NEON_REDUCE_MINMAX
// 定义宏 NPY_IMPL_NEON_REDUCE_MINMAX,用于实现 NEON 指令集的最小值和最大值归约函数
// 定义内联函数,用于将一个 npyv_f32 向量归约为一个 float 最小值或最大值 \
NPY_FINLINE float npyv_reduce_
{ \
// 使用 NEON 指令将输入向量 a 的低位和高位部分合并,得到一个 float32x2_t 结果向量 \
float32x2_t r = vp
// 使用 NEON 指令再次对合并结果向量 r 进行 INTRIN 运算,得到最终归约结果向量 \
r = vp
// 返回最终结果向量的第一个元素,即最小值或最大值 \
return vget_lane_f32(r, 0); \
} \
// 定义内联函数,用于将一个 npyv_f32 向量归约为一个 float 最小值或最大值,忽略 NaN 值 \
NPY_FINLINE float npyv_reduce_
{ \
// 获取非 NaN 元素的掩码 \
npyv_b32 notnan = npyv_notnan_f32(a); \
// 如果向量中所有元素均为 NaN,则直接返回第一个元素的值 \
if (NPY_UNLIKELY(!npyv_any_b32(notnan))) { \
return vgetq_lane_f32(a, 0); \
} \
// 使用掩码选择非 NaN 元素,将 NaN 元素替换为 INF (表示无穷大) \
a = npyv_select_f32(notnan, a, \
npyv_reinterpret_f32_u32(npyv_setall_u32(INF))); \
// 调用相应的归约函数处理已处理过 NaN 的向量 a,返回最终的最小值或最大值 \
return npyv_reduce_
} \
// 定义内联函数,用于将一个 npyv_f32 向量归约为一个 float 最小值或最大值,无论是否有 NaN 值 \
NPY_FINLINE float npyv_reduce_
{ \
// 直接调用归约函数处理向量 a,返回最终的最小值或最大值 \
return npyv_reduce_
}
// 取消宏定义 NPY_IMPL_NEON_REDUCE_MINMAX 的定义
NPY_FINLINE STYPE npyv_reduce_
{ \
// 提取 NEON 向量 a 的低部分并将其转换为 STYPE 类型
STYPE al = (STYPE)vget_low_
// 提取 NEON 向量 a 的高部分并将其转换为 STYPE 类型
STYPE ah = (STYPE)vget_high_
// 返回 al 和 ah 中较大或较小的值,根据 OP 参数指定的比较操作符
return al OP ah ? al : ah; \
}
// 定义 NEON 实现的最大值和最小值归约函数
NPY_FINLINE STYPE npyv_reduce_
{ \
// 提取 NEON 向量 a 的低部分并将其转换为 STYPE 类型
STYPE al = (STYPE)vget_low_
// 提取 NEON 向量 a 的高部分并将其转换为 STYPE 类型
STYPE ah = (STYPE)vget_high_
// 返回 al 和 ah 中较大或较小的值,根据 OP 参数指定的比较操作符
return al OP ah ? al : ah; \
}
// 调用宏定义来生成具体的函数实现,用于不同的数据类型和操作符
NPY_IMPL_NEON_REDUCE_MINMAX(max, npy_uint64, u64, >)
NPY_IMPL_NEON_REDUCE_MINMAX(max, npy_int64, s64, >)
NPY_IMPL_NEON_REDUCE_MINMAX(min, npy_uint64, u64, <)
NPY_IMPL_NEON_REDUCE_MINMAX(min, npy_int64, s64, <)
// round to nearest integer even
NPY_FINLINE npyv_f32 npyv_rint_f32(npyv_f32 a)
{
// 使用 NEON 指令 vrndnq_f32 对向量 a 进行舍入到最近的偶数整数
return vrndnq_f32(a);
// ARMv7 NEON 仅支持浮点数到整数的截断转换。
// 使用一个魔术技巧,添加 1.5 * 2^23 来实现舍入到最近的偶数整数,
// 然后减去这个魔术数以得到整数部分。
// 创建一个常数向量,内容为 -0.0f 的无符号整数表示
const npyv_u32 szero = vreinterpretq_u32_f32(vdupq_n_f32(-0.0f));
// 计算向量 a 的符号位掩码
const npyv_u32 sign_mask = vandq_u32(vreinterpretq_u32_f32(a), szero);
// 创建一个常数向量,内容为 2^23 的浮点数表示
const npyv_f32 two_power_23 = vdupq_n_f32(8388608.0); // 2^23
// 创建一个常数向量,内容为 1.5 * 2^23 的浮点数表示
const npyv_f32 two_power_23h = vdupq_n_f32(12582912.0f); // 1.5 * 2^23
// 创建一个向量,用于消除 NaN 值,避免无效的浮点错误
npyv_u32 nnan_mask = vceqq_f32(a, a);
// 计算向量 a 的绝对值
npyv_f32 abs_x = vabsq_f32(vreinterpretq_f32_u32(vandq_u32(nnan_mask, vreinterpretq_u32_f32(a))));
// 执行舍入操作,通过添加魔术数 1.5 * 2^23
npyv_f32 round = vsubq_f32(vaddq_f32(two_power_23h, abs_x), two_power_23h);
// 使用符号掩码来进行符号位复制
round = vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(round), sign_mask ));
// 如果 |a| >= 2^23 或者 a 是 NaN,则返回 a;否则返回 round
npyv_u32 mask = vcleq_f32(abs_x, two_power_23);
mask = vandq_u32(mask, nnan_mask);
return vbslq_f32(mask, round, a);
}
// 如果 NPY_SIMD_F64 定义了,则使用 vrndnq_f64 来定义 npyv_rint_f64 函数
// 如果 NPY_HAVE_ASIMD 定义了,则使用 vrndpq_f32 来定义 npyv_ceil_f32 函数
// 否则,定义一个函数 npyv_ceil_f32,实现向上取整操作
NPY_FINLINE npyv_f32 npyv_ceil_f32(npyv_f32 a)
{
// 创建常量 one,其值为单精度浮点数 1.0 的转换后的无符号整数表示
const npyv_u32 one = vreinterpretq_u32_f32(vdupq_n_f32(1.0f));
// 创建常量 szero,其值为单精度浮点数 -0.0 的转换后的无符号整数表示
const npyv_u32 szero = vreinterpretq_u32_f32(vdupq_n_f32(-0.0f));
// 创建 sign_mask,通过将浮点数 a 转换为无符号整数,并与 szero 相与得到
const npyv_u32 sign_mask = vandq_u32(vreinterpretq_u32_f32(a), szero);
// 创建常量 two_power_23,其值为单精度浮点数 8388608.0 的复制
const npyv_f32 two_power_23 = vdupq_n_f32(8388608.0); // 2^23
// 创建常量 two_power_23h,其值为单精度浮点数 12582912.0 的复制
const npyv_f32 two_power_23h = vdupq_n_f32(12582912.0f); // 1.5 * 2^23
// 创建 nnan_mask,检查 a 是否等于自身(排除 NaN 值)
npyv_u32 nnan_mask = vceqq_f32(a, a);
// 将 nnan_mask 与 a 进行位与操作,得到 x,用于消除 NaN 值以避免无效的浮点数错误
npyv_f32 x = vreinterpretq_f32_u32(vandq_u32(nnan_mask, vreinterpretq_u32_f32(a)));
// 计算 x 的绝对值 abs_x
npyv_f32 abs_x = vabsq_f32(x);
// 使用魔数 1.5 * 2^23 进行四舍五入
npyv_f32 round = vsubq_f32(vaddq_f32(two_power_23h, abs_x), two_power_23h);
// 将 round 的符号位设置为与 a 相同的符号
round = vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(round), sign_mask));
// 对 round 进行向上取整操作,考虑了有符号零值的情况
npyv_f32 ceil = vaddq_f32(round, vreinterpretq_f32_u32(
vandq_u32(vcltq_f32(round, x), one))
);
// 将 ceil 的符号位设置为与 a 相同的符号
ceil = vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(ceil), sign_mask));
// 如果 |a| >= 2^23 或者 a 为 NaN,则返回 a;否则返回 ceil
npyv_u32 mask = vcleq_f32(abs_x, two_power_23);
mask = vandq_u32(mask, nnan_mask);
return vbslq_f32(mask, ceil, a);
}
// trunc
// 定义在没有 ASIMD 支持时,用于截断操作的函数 npyv_trunc_f32
NPY_FINLINE npyv_f32 npyv_trunc_f32(npyv_f32 a)
{
// 定义常量 max_int 为 0x7fffffff
const npyv_s32 max_int = vdupq_n_s32(0x7fffffff);
// 定义常量 exp_mask 为 0xff000000
const npyv_u32 exp_mask = vdupq_n_u32(0xff000000);
// 定义常量 szero 为 -0.0f 的整数表示
const npyv_s32 szero = vreinterpretq_s32_f32(vdupq_n_f32(-0.0f));
// 创建 sign_mask,用于提取输入参数 a 的符号位
const npyv_u32 sign_mask = vandq_u32(
vreinterpretq_u32_f32(a), vreinterpretq_u32_s32(szero));
// 创建 nfinite_mask,用于检测 a 是否为有限值
npyv_u32 nfinite_mask = vshlq_n_u32(vreinterpretq_u32_f32(a), 1);
nfinite_mask = vandq_u32(nfinite_mask, exp_mask);
nfinite_mask = vceqq_u32(nfinite_mask, exp_mask);
// 消除 NaN 和 inf,避免无效的浮点错误
npyv_f32 x = vreinterpretq_f32_u32(
veorq_u32(nfinite_mask, vreinterpretq_u32_f32(a)));
/**
* 在 armv7 上,vcvtq.f32 处理特殊情况如下:
* NaN 返回 0
* +inf 或超出范围 返回 0x80000000(-0.0f)
* -inf 或超出范围 返回 0x7fffffff(nan)
*/
// 将 x 转为整数类型
npyv_s32 trunci = vcvtq_s32_f32(x);
// 将整数类型再转回浮点数类型
npyv_f32 trunc = vcvtq_f32_s32(trunci);
// 根据符号位,保留有符号零,例如 -0.5 -> -0.0
trunc = vreinterpretq_f32_u32(
vorrq_u32(vreinterpretq_u32_f32(trunc), sign_mask));
// 如果溢出,则返回原始参数 a
npyv_u32 overflow_mask = vorrq_u32(
vceqq_s32(trunci, szero), vceqq_s32(trunci, max_int)
);
// 如果溢出或非有限值,则返回原始参数 a,否则返回截断后的值
return vbslq_f32(vorrq_u32(nfinite_mask, overflow_mask), a, trunc);
}
// floor
// 在没有 ASIMD 支持时,定义用于向下取整操作的函数 npyv_floor_f32
NPY_FINLINE npyv_f32 npyv_floor_f32(npyv_f32 a)
{
// 创建一个常量,其值为单精度浮点数 1.0 对应的无符号整数形式
const npyv_u32 one = vreinterpretq_u32_f32(vdupq_n_f32(1.0f));
// 创建一个常量,其值为单精度浮点数 -0.0 对应的无符号整数形式
const npyv_u32 szero = vreinterpretq_u32_f32(vdupq_n_f32(-0.0f));
// 通过按位与操作,生成一个用于标记符号位的掩码
const npyv_u32 sign_mask = vandq_u32(vreinterpretq_u32_f32(a), szero);
// 创建一个常量,其值为单精度浮点数 2^23 对应的向量形式
const npyv_f32 two_power_23 = vdupq_n_f32(8388608.0); // 2^23
// 创建一个常量,其值为单精度浮点数 1.5 * 2^23 对应的向量形式
const npyv_f32 two_power_23h = vdupq_n_f32(12582912.0f); // 1.5 * 2^23
// 创建一个掩码,用于消除 NaN 值,以避免无效的浮点错误
npyv_u32 nnan_mask = vceqq_f32(a, a);
// 通过按位与操作,提取绝对值形式的浮点数向量
npyv_f32 x = vreinterpretq_f32_u32(vandq_u32(nnan_mask, vreinterpretq_u32_f32(a)));
// 计算绝对值的浮点数向量
npyv_f32 abs_x = vabsq_f32(x);
// 通过加上魔数 1.5 * 2^23 来进行四舍五入
npyv_f32 round = vsubq_f32(vaddq_f32(two_power_23h, abs_x), two_power_23h);
// 执行拷贝符号操作
round = vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(round), sign_mask));
// 计算向下取整的浮点数向量
npyv_f32 floor = vsubq_f32(round, vreinterpretq_f32_u32(
vandq_u32(vcgtq_f32(round, x), one)
));
// 尊重带符号零的特性
floor = vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(floor), sign_mask));
// 如果 |a| >= 2^23 或者 a 是 NaN,则返回 a;否则返回 floor
npyv_u32 mask = vcleq_f32(abs_x, two_power_23);
mask = vandq_u32(mask, nnan_mask);
return vbslq_f32(mask, floor, a);
}
// 如果定义了 NPY_HAVE_ASIMD 宏,则执行以下代码块
// 如果定义了 NPY_SIMD_F64 宏,则定义 npyv_floor_f64 宏为 vrndmq_f64
// 结束对 NPY_HAVE_ASIMD 宏的条件编译
// 结束对 _NPY_SIMD_NEON_MATH_H 头文件的条件编译
.\numpy\numpy\_core\src\common\simd\neon\memory.h
// 如果未定义 NPY_SIMD 宏,则报错,此头文件不是独立可用的
/***************************
* load/store
***************************/
// GCC 需要对指针类型进行明确的字面类型定义,否则会导致模棱两可的错误
// 加载操作,从 ptr 指向的地址加载数据到向量 npyv_
NPY_FINLINE npyv_
{ return vld1q_
// 加载操作,从 ptr 指向的地址加载数据到向量 npyv_
NPY_FINLINE npyv_
{ return vld1q_
// 加载操作,从 ptr 指向的地址加载数据到向量 npyv_
NPY_FINLINE npyv_
{ return vld1q_
// 加载操作,从 ptr 指向的地址加载数据到向量 npyv_
NPY_FINLINE npyv_
{ \
return vcombine_
vld1_
); \
} \
// 存储操作,将向量 vec 的数据存储到 ptr 指向的地址
NPY_FINLINE void npyv_store_
{ vst1q_
// 存储操作,将向量 vec 的数据存储到 ptr 指向的地址
NPY_FINLINE void npyv_storea_
{ vst1q_
// 存储操作,将向量 vec 的数据存储到 ptr 指向的地址
NPY_FINLINE void npyv_stores_
{ vst1q_
// 存储操作,将向量 vec 的低位数据存储到 ptr 指向的地址
NPY_FINLINE void npyv_storel_
{ vst1_
// 存储操作,将向量 vec 的高位数据存储到 ptr 指向的地址
NPY_FINLINE void npyv_storeh_
{ vst1_
// 定义各种数据类型的 NEON 内存操作宏
NPYV_IMPL_NEON_MEM(u8, uint8_t)
NPYV_IMPL_NEON_MEM(s8, int8_t)
NPYV_IMPL_NEON_MEM(u16, uint16_t)
NPYV_IMPL_NEON_MEM(s16, int16_t)
NPYV_IMPL_NEON_MEM(u32, uint32_t)
NPYV_IMPL_NEON_MEM(s32, int32_t)
NPYV_IMPL_NEON_MEM(u64, uint64_t)
NPYV_IMPL_NEON_MEM(s64, int64_t)
NPYV_IMPL_NEON_MEM(f32, float)
NPYV_IMPL_NEON_MEM(f64, double)
/***************************
* Non-contiguous Load
***************************/
// 非连续加载操作,从 ptr 指向的地址开始,按照给定的步长 stride 加载数据到向量 npyv_s32 中
NPY_FINLINE npyv_s32 npyv_loadn_s32(const npy_int32 *ptr, npy_intp stride)
{
// 创建一个全为 0 的 128 位整型 NEON 向量
int32x4_t a = vdupq_n_s32(0);
// 分别从不同地址加载数据到向量的不同通道
a = vld1q_lane_s32((const int32_t*)ptr, a, 0);
a = vld1q_lane_s32((const int32_t*)ptr + stride, a, 1);
a = vld1q_lane_s32((const int32_t*)ptr + stride*2, a, 2);
a = vld1q_lane_s32((const int32_t*)ptr + stride*3, a, 3);
这段代码主要是 NEON SIMD 操作的宏定义和函数实现,用于在 ARM 架构下进行内存的加载和存储操作,支持不同数据类型和非连续加载。
return a;
//// 64
NPY_FINLINE npyv_s64 npyv_loadn_s64(const npy_int64 *ptr, npy_intp stride)
{
// 使用 Neon 指令加载两个 int64_t 元素到一个 128 位寄存器中
return vcombine_s64(
vld1_s64((const int64_t*)ptr), vld1_s64((const int64_t*)ptr + stride)
);
}
NPY_FINLINE npyv_u64 npyv_loadn_u64(const npy_uint64 *ptr, npy_intp stride)
{
// 将加载的 int64_t 数据重新解释为 uint64_t 数据
return npyv_reinterpret_u64_s64(
npyv_loadn_s64((const npy_int64*)ptr, stride)
);
}
NPY_FINLINE npyv_f64 npyv_loadn_f64(const double *ptr, npy_intp stride)
{
// 将加载的 int64_t 数据重新解释为 double 数据
return npyv_reinterpret_f64_s64(
npyv_loadn_s64((const npy_int64*)ptr, stride)
);
}
//// 64-bit load over 32-bit stride
NPY_FINLINE npyv_u32 npyv_loadn2_u32(const npy_uint32 *ptr, npy_intp stride)
{
// 使用 Neon 指令加载两个 uint32_t 元素到一个 64 位寄存器中
return vcombine_u32(
vld1_u32((const uint32_t*)ptr), vld1_u32((const uint32_t*)ptr + stride)
);
}
NPY_FINLINE npyv_s32 npyv_loadn2_s32(const npy_int32 *ptr, npy_intp stride)
{
// 将加载的 uint32_t 数据重新解释为 int32_t 数据
return npyv_reinterpret_s32_u32(npyv_loadn2_u32((const npy_uint32*)ptr, stride));
}
NPY_FINLINE npyv_f32 npyv_loadn2_f32(const float *ptr, npy_intp stride)
{
// 将加载的 uint32_t 数据重新解释为 float 数据
return npyv_reinterpret_f32_u32(npyv_loadn2_u32((const npy_uint32*)ptr, stride));
}
//// 128-bit load over 64-bit stride
NPY_FINLINE npyv_u64 npyv_loadn2_u64(const npy_uint64 *ptr, npy_intp stride)
{
// 无需考虑 stride,直接加载 uint64_t 数据
(void)stride; return npyv_load_u64(ptr);
}
NPY_FINLINE npyv_s64 npyv_loadn2_s64(const npy_int64 *ptr, npy_intp stride)
{
// 无需考虑 stride,直接加载 int64_t 数据
(void)stride; return npyv_load_s64(ptr);
}
NPY_FINLINE npyv_f64 npyv_loadn2_f64(const double *ptr, npy_intp stride)
{
// 无需考虑 stride,直接加载 double 数据
(void)stride; return npyv_load_f64(ptr);
}
/***************************
* Non-contiguous Store
***************************/
//// 32
NPY_FINLINE void npyv_storen_s32(npy_int32 *ptr, npy_intp stride, npyv_s32 a)
{
// 分别存储 4 个 int32_t 元素到指定位置,考虑 stride
vst1q_lane_s32((int32_t*)ptr, a, 0);
vst1q_lane_s32((int32_t*)ptr + stride, a, 1);
vst1q_lane_s32((int32_t*)ptr + stride*2, a, 2);
vst1q_lane_s32((int32_t*)ptr + stride*3, a, 3);
}
NPY_FINLINE void npyv_storen_u32(npy_uint32 *ptr, npy_intp stride, npyv_u32 a)
{
// 将存储的 int32_t 数据重新解释为 uint32_t 数据
npyv_storen_s32((npy_int32*)ptr, stride, npyv_reinterpret_s32_u32(a));
}
NPY_FINLINE void npyv_storen_f32(float *ptr, npy_intp stride, npyv_f32 a)
{
// 将存储的 int32_t 数据重新解释为 float 数据
npyv_storen_s32((npy_int32*)ptr, stride, npyv_reinterpret_s32_f32(a));
}
//// 64
NPY_FINLINE void npyv_storen_s64(npy_int64 *ptr, npy_intp stride, npyv_s64 a)
{
// 分别存储 2 个 int64_t 元素到指定位置,考虑 stride
vst1q_lane_s64((int64_t*)ptr, a, 0);
vst1q_lane_s64((int64_t*)ptr + stride, a, 1);
}
NPY_FINLINE void npyv_storen_u64(npy_uint64 *ptr, npy_intp stride, npyv_u64 a)
{
// 将存储的 int64_t 数据重新解释为 uint64_t 数据
npyv_storen_s64((npy_int64*)ptr, stride, npyv_reinterpret_s64_u64(a));
}
NPY_FINLINE void npyv_storen_f64(double *ptr, npy_intp stride, npyv_f64 a)
//// 64-bit store over 32-bit stride
NPY_FINLINE void npyv_storen2_u32(npy_uint32 *ptr, npy_intp stride, npyv_u32 a)
{
// 使用 SIMD 指令将第一个 64 位数据存储到 ptr,a 被重新解释为 64 位数据
vst1q_lane_u64((uint64_t*)ptr, npyv_reinterpret_u64_u32(a), 0);
// 使用 SIMD 指令将第二个 64 位数据存储到 ptr + stride,a 被重新解释为 64 位数据
vst1q_lane_u64((uint64_t*)(ptr + stride), npyv_reinterpret_u64_u32(a), 1);
// 在 armhf 环境中,要求对齐存储,将 a 的低 32 位存储到 ptr
vst1_u32((uint32_t*)ptr, vget_low_u32(a));
// 在 armhf 环境中,将 a 的高 32 位存储到 ptr + stride
vst1_u32((uint32_t*)ptr + stride, vget_high_u32(a));
}
NPY_FINLINE void npyv_storen2_s32(npy_int32 *ptr, npy_intp stride, npyv_s32 a)
{
// 将 npyv_s32 类型数据 a 重新解释为 npyv_u32 并进行存储
npyv_storen2_u32((npy_uint32*)ptr, stride, npyv_reinterpret_u32_s32(a));
}
NPY_FINLINE void npyv_storen2_f32(float *ptr, npy_intp stride, npyv_f32 a)
{
// 将 npyv_f32 类型数据 a 重新解释为 npyv_u32 并进行存储
npyv_storen2_u32((npy_uint32*)ptr, stride, npyv_reinterpret_u32_f32(a));
}
//// 128-bit store over 64-bit stride
NPY_FINLINE void npyv_storen2_u64(npy_uint64 *ptr, npy_intp stride, npyv_u64 a)
{
// 忽略 stride 参数,直接存储 a 的数据到 ptr
(void)stride;
npyv_store_u64(ptr, a);
}
NPY_FINLINE void npyv_storen2_s64(npy_int64 *ptr, npy_intp stride, npyv_s64 a)
{
// 忽略 stride 参数,直接存储 a 的数据到 ptr
(void)stride;
npyv_store_s64(ptr, a);
}
NPY_FINLINE void npyv_storen2_f64(double *ptr, npy_intp stride, npyv_f64 a)
{
// 忽略 stride 参数,直接存储 a 的数据到 ptr
(void)stride;
npyv_store_f64(ptr, a);
}
/*********************************
* Partial Load
*********************************/
//// 32
NPY_FINLINE npyv_s32 npyv_load_till_s32(const npy_int32 *ptr, npy_uintp nlane, npy_int32 fill)
{
assert(nlane > 0);
npyv_s32 a;
switch(nlane) {
case 1:
// 加载单个 32 位数据到 a,使用 fill 填充剩余的 SIMD 矢量
a = vld1q_lane_s32((const int32_t*)ptr, vdupq_n_s32(fill), 0);
break;
case 2:
// 组合两个 32 位数据到 a,并使用 fill 填充剩余的 SIMD 矢量
a = vcombine_s32(vld1_s32((const int32_t*)ptr), vdup_n_s32(fill));
break;
case 3:
// 组合前两个 32 位数据到 a,第三个数据使用 fill 填充剩余的 SIMD 矢量
a = vcombine_s32(
vld1_s32((const int32_t*)ptr),
vld1_lane_s32((const int32_t*)ptr + 2, vdup_n_s32(fill), 0)
);
break;
default:
// 加载所有的 32 位数据到 a
return npyv_load_s32(ptr);
}
// 如果定义了 NPY_SIMD_GUARD_PARTIAL_LOAD,执行一个 workaround 操作
volatile npyv_s32 workaround = a;
a = vorrq_s32(workaround, a);
return a;
}
// 使用 0 填充剩余的 SIMD 矢量
NPY_FINLINE npyv_s32 npyv_load_tillz_s32(const npy_int32 *ptr, npy_uintp nlane)
{
return npyv_load_till_s32(ptr, nlane, 0);
}
//// 64
NPY_FINLINE npyv_s64 npyv_load_till_s64(const npy_int64 *ptr, npy_uintp nlane, npy_int64 fill)
{
assert(nlane > 0);
if (nlane == 1) {
// 加载单个 64 位数据到 a,并使用 fill 填充剩余的 SIMD 矢量
npyv_s64 a = vcombine_s64(vld1_s64((const int64_t*)ptr), vdup_n_s64(fill));
// 如果定义了 NPY_SIMD_GUARD_PARTIAL_LOAD,执行一个 workaround 操作
volatile npyv_s64 workaround = a;
a = vorrq_s64(workaround, a);
return a;
}
// 加载所有的 64 位数据到 a
return npyv_load_s64(ptr);
}
// 使用 0 填充剩余的 SIMD 矢量
NPY_FINLINE npyv_s64 npyv_load_tillz_s64(const npy_int64 *ptr, npy_uintp nlane)
{
return npyv_load_till_s64(ptr, nlane, 0);
}
//// 64-bit nlane
NPY_FINLINE npyv_s32 npyv_load2_till_s32(const npy_int32 *ptr, npy_uintp nlane,
npy_int32 fill_lo, npy_int32 fill_hi)
{
assert(nlane > 0);
// 加载 nlane 个 32 位数据到 a,同时使用 fill_lo 和 fill_hi 填充剩余的 SIMD 矢量
// 确保 nlane 大于 0
assert(nlane > 0);
npyv_s32 a;
switch(nlane) {
case 1:
// 加载一个 32 位数据到 a,并用 fill_lo 和 fill_hi 填充剩余的 SIMD 矢量
a = vld1q_lane_s32((const int32_t*)ptr, vcombine_s32(vdup_n_s32(fill_lo), vdup_n_s32(fill_hi)), 0);
break;
case 2:
// 加载两个 32 位数据到 a,并用 fill_hi 填充剩余的 SIMD 矢量
a = vcombine_s32(vld1_s32((const int32_t*)ptr), vdup_n_s32(fill_hi));
break;
case 3:
// 加载前两个 32 位数据到 a,第三个数据使用 fill_hi 填充剩余的 SIMD 矢量
a = vcombine_s32(
vld1_s32((const int32_t*)ptr),
vld1_lane_s32((const int32_t*)ptr + 2, vdup_n_s32(fill_hi), 0)
);
break;
default:
// 加载所有的 32 位数据到 a
return npyv_load_s32(ptr);
}
// 如果定义了 NPY_SIMD_GUARD_PARTIAL_LOAD,执行一个 workaround 操作
volatile npyv_s32 workaround = a;
a = vorrq_s32(workaround, a);
return a;
}
if (nlane == 1) {
const int32_t NPY_DECL_ALIGNED(16) fill[2] = {fill_lo, fill_hi};
npyv_s32 a = vcombine_s32(vld1_s32((const int32_t*)ptr), vld1_s32(fill));
volatile npyv_s32 workaround = a;
a = vorrq_s32(workaround, a);
return a;
}
return npyv_load_s32(ptr);
//// 128-bit nlane
// 加载指定长度的 int64 数据到 SIMD 寄存器中,并在剩余的位置填充零值
NPY_FINLINE npyv_s64 npyv_load2_till_s64(const npy_int64 *ptr, npy_uintp nlane,
npy_int64 fill_lo, npy_int64 fill_hi)
{
// 忽略未使用的参数 fill_lo 和 fill_hi,直接加载所有数据到寄存器中
(void)nlane; (void)fill_lo; (void)fill_hi;
return npyv_load_s64(ptr); // 调用加载 int64 数据的函数并返回结果
}
NPY_FINLINE npyv_s64 npyv_load2_tillz_s64(const npy_int64 *ptr, npy_uintp nlane)
{
// 忽略未使用的参数 nlane,加载指定长度的 int64 数据到 SIMD 寄存器中,并在剩余的位置填充零值
(void)nlane;
return npyv_load_s64(ptr); // 调用加载 int64 数据的函数并返回结果
}
/*********************************
* Non-contiguous partial load
*********************************/
//// 32
// 加载不连续的部分 int32 数据到 SIMD 寄存器中
NPY_FINLINE npyv_s32
npyv_loadn_till_s32(const npy_int32 *ptr, npy_intp stride, npy_uintp nlane, npy_int32 fill)
{
assert(nlane > 0); // 断言加载长度大于零
int32x4_t vfill = vdupq_n_s32(fill); // 使用 fill 参数创建一个 int32x4_t 类型的常量向量
switch(nlane) {
case 3:
vfill = vld1q_lane_s32((const int32_t*)ptr + stride*2, vfill, 2); // 加载第三个元素到向量中的第二个位置
case 2:
vfill = vld1q_lane_s32((const int32_t*)ptr + stride, vfill, 1); // 加载第二个元素到向量中的第一个位置
case 1:
vfill = vld1q_lane_s32((const int32_t*)ptr, vfill, 0); // 加载第一个元素到向量中的第零个位置
break;
default:
return npyv_loadn_s32(ptr, stride); // 加载连续的 int32 数据到 SIMD 寄存器中
}
volatile npyv_s32 workaround = vfill; // 如果定义了 NPY_SIMD_GUARD_PARTIAL_LOAD,则执行的虚拟访问以避免优化
vfill = vorrq_s32(workaround, vfill); // 使用或运算以确保加载数据到 vfill 向量中
return vfill; // 返回加载后的向量
}
// 加载指定长度的 int32 数据到 SIMD 寄存器中,并在剩余的位置填充零值
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); // 调用加载不连续部分 int32 数据的函数,填充零值并返回结果
}
// 加载指定长度的 int64 数据到 SIMD 寄存器中,并在剩余的位置填充指定的值
NPY_FINLINE npyv_s64
npyv_loadn_till_s64(const npy_int64 *ptr, npy_intp stride, npy_uintp nlane, npy_int64 fill)
{
assert(nlane > 0); // 断言加载长度大于零
if (nlane == 1) {
return npyv_load_till_s64(ptr, 1, fill); // 如果加载长度为 1,则加载到 SIMD 寄存器中并填充指定的值
}
return npyv_loadn_s64(ptr, stride); // 否则加载连续的 int64 数据到 SIMD 寄存器中
}
// 加载指定长度的 int64 数据到 SIMD 寄存器中,并在剩余的位置填充零值
NPY_FINLINE npyv_s64 npyv_loadn_tillz_s64(const npy_int64 *ptr, npy_intp stride, npy_uintp nlane)
{
return npyv_loadn_till_s64(ptr, stride, nlane, 0); // 调用加载不连续部分 int64 数据的函数,填充零值并返回结果
}
//// 64-bit load over 32-bit stride
// 使用 32 位步长加载指定长度的 int32 数据到 SIMD 寄存器中
NPY_FINLINE npyv_s32 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); // 断言加载长度大于零
if (nlane == 1) {
const int32_t NPY_DECL_ALIGNED(16) fill[2] = {fill_lo, fill_hi}; // 声明一个填充数组,确保对齐到 16 字节边界
npyv_s32 a = vcombine_s32(vld1_s32((const int32_t*)ptr), vld1_s32(fill)); // 使用填充值创建一个 int32x2_t 向量并与原始数据合并
volatile npyv_s32 workaround = a; // 如果定义了 NPY_SIMD_GUARD_PARTIAL_LOAD,则执行的虚拟访问以避免优化
a = vorrq_s32(workaround, a); // 使用或运算以确保加载数据到 a 向量中
return a; // 返回加载后的向量
}
return npyv_loadn2_s32(ptr, stride); // 加载连续的 int32 数据到 SIMD 寄存器中
}
// 使用 32 位步长加载指定长度的 int32 数据到 SIMD 寄存器中,并在剩余的位置填充零值
NPY_FINLINE npyv_s32 npyv_loadn2_tillz_s32(const npy_int32 *ptr, npy_intp stride, npy_uintp nlane)
{
assert(nlane > 0); // 断言加载长度大于零
if (nlane == 1) {
npyv_s32 a = vcombine_s32(vld1_s32((const int32_t*)ptr), vdup_n_s32(0)); // 使用零值创建一个 int32x2_t 向量并与原始数据合并
volatile npyv_s32 workaround = a; // 如果定义了 NPY_SIMD_GUARD_PARTIAL_LOAD,则执行的虚拟访问以避免优化
a = vorrq_s32(workaround, a); // 使用或运算以确保加载数据到 a 向量中
return a; // 返回加载后的向量
}
return npyv_loadn2_s32(ptr, stride); // 加载连续的 int32 数据到 SIMD 寄存器中
}
/*********************************
* Non-contiguous partial store
*********************************/
//// 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)
{
assert(nlane > 0); // 断言确保 nlane 大于 0
// 将第一个 lane 的值存储到 ptr 指向的地址
vst1q_lane_s32((int32_t*)ptr, a, 0);
switch(nlane) {
case 1:
return; // 如果 nlane 为 1,直接返回,只存储了一个元素
case 2:
// 将第二个 lane 的值存储到 ptr + stride 指向的地址
vst1q_lane_s32((int32_t*)ptr + stride, a, 1);
return;
case 3:
// 将第二个 lane 的值存储到 ptr + stride 指向的地址
vst1q_lane_s32((int32_t*)ptr + stride, a, 1);
// 将第三个 lane 的值存储到 ptr + stride*2 指向的地址
vst1q_lane_s32((int32_t*)ptr + stride*2, a, 2);
return;
default:
// 默认情况下,存储所有的 lanes 的值到 ptr + stride*i 指向的地址
vst1q_lane_s32((int32_t*)ptr + stride, a, 1);
vst1q_lane_s32((int32_t*)ptr + stride*2, a, 2);
vst1q_lane_s32((int32_t*)ptr + stride*3, a, 3);
}
}
assert(nlane > 0);
// 如果定义了 NPY_SIMD_F64,使用 Neon 指令将 s32 类型向量 a 转换为 s64 类型后存储到内存 ptr
vst1q_lane_s64((int64_t*)ptr, npyv_reinterpret_s64_s32(a), 0);
// 如果向量长度 nlane 大于 1,继续将向量 a 的第一个 64 位元素存储到 ptr + stride 处
if (nlane > 1) {
vst1q_lane_s64((int64_t*)(ptr + stride), npyv_reinterpret_s64_s32(a), 1);
}
// 如果未定义 NPY_SIMD_F64,将 s32 类型向量 a 的低 32 位元素存储到 ptr 处
npyv_storel_s32(ptr, a);
// 如果向量长度 nlane 大于 1,将向量 a 的高 32 位元素存储到 ptr + stride 处
if (nlane > 1) {
npyv_storeh_s32(ptr + stride, a);
}
}
//// 128-bit store over 64-bit stride
// 将长度为 nlane 的 s64 类型向量 a 存储到内存 ptr,步长为 stride
NPY_FINLINE void npyv_storen2_till_s64(npy_int64 *ptr, npy_intp stride, npy_uintp nlane, npyv_s64 a)
{ assert(nlane > 0); (void)stride; (void)nlane; npyv_store_s64(ptr, a); }
/*****************************************************************
* Implement partial load/store for u32/f32/u64/f64... via casting
*****************************************************************/
// 定义宏 NPYV_IMPL_NEON_REST_PARTIAL_TYPES,用于实现通过类型转换实现部分加载/存储操作
// 部分加载函数:从 ptr 处加载长度为 nlane 的 F_SFX 类型向量,用 fill 填充空缺部分
NPY_FINLINE npyv_
(const npyv_lanetype_
{ \
// 联合体 pun,将 fill 转换为对应的 T_SFX 类型后再转换为 F_SFX 类型向量返回
union { \
npyv_lanetype_
npyv_lanetype_
} pun; \
pun.from_
return npyv_reinterpret_
(const npyv_lanetype_
)); \
} \
// 部分加载函数,带步长版本:从 ptr 处以 stride 步长加载长度为 nlane 的 F_SFX 类型向量
NPY_FINLINE npyv_
(const npyv_lanetype_
npyv_lanetype_
{ \
union { \
npyv_lanetype_
npyv_lanetype_
} pun; \
pun.from_
// 使用联合体 pun 将 fill 转换为对应的类型 from_
return npyv_reinterpret_
(const npyv_lanetype_
)); \
} \
// 以填充值 fill 加载数据直到最后的函数定义,将 F_SFX 类型指针 ptr 转换为 T_SFX 类型
NPY_FINLINE npyv_
(const npyv_lanetype_
{ \
// 调用 load_tillz_
return npyv_reinterpret_
(const npyv_lanetype_
)); \
} \
// 以填充值为零加载数据直到最后的函数定义,将 F_SFX 类型指针 ptr 转换为 T_SFX 类型
NPY_FINLINE npyv_
(const npyv_lanetype_
{ \
// 调用 loadn_tillz_
return npyv_reinterpret_
(const npyv_lanetype_
)); \
} \
// 将数据存储直到最后的函数定义,将 F_SFX 类型指针 ptr 中的数据存储为 T_SFX 类型
NPY_FINLINE void npyv_store_till_
(npyv_lanetype_
{ \
// 调用 store_till_
npyv_store_till_
(npyv_lanetype_
npyv_reinterpret_
); \
} \
NPY_FINLINE void npyv_storen_till_
(npyv_lanetype_
{ \
// 定义一个内联函数,用于将长度为 nlane 的 npyv_
npyv_storen_till_
// 将 npyv_
(npyv_lanetype_
npyv_reinterpret_
); \
}
// 定义宏 NPYV_IMPL_NEON_REST_PARTIAL_TYPES_PAIR,用于生成两个函数:
// - npyv_load2_till_
// 使用 fill_lo 和 fill_hi 分别填充未加载的元素
// - npyv_loadn2_till_
// 使用 fill_lo 和 fill_hi 填充未加载的元素
// 内联函数 npyv_load2_till_
// 使用 fill_lo 和 fill_hi 分别填充未加载的元素
NPY_FINLINE npyv_
(const npyv_lanetype_
npyv_lanetype_
{ \
// 联合 pun 用于类型转换
union pun { \
npyv_lanetype_
npyv_lanetype_
}; \
union pun pun_lo; \
union pun pun_hi; \
pun_lo.from_
pun_hi.from_
// 调用 npyv_load2_till_
// 这里将指针 ptr 强制转换为指向类型为 T_SFX 的元素的指针,并传入填充值 pun_lo.to_
return npyv_reinterpret_
(const npyv_lanetype_
)); \
} \
// 内联函数 npyv_loadn2_till_
// 使用 fill_lo 和 fill_hi 填充未加载的元素
NPY_FINLINE npyv_
(const npyv_lanetype_
npyv_lanetype_
{ \
union pun { \ // 定义一个联合体 pun,用于类型 F_SFX 和 T_SFX 的转换
npyv_lanetype_
npyv_lanetype_
}; \
union pun pun_lo; \ // 声明 pun 结构体变量 pun_lo
union pun pun_hi; \ // 声明 pun 结构体变量 pun_hi
pun_lo.from_
pun_hi.from_
return npyv_reinterpret_
(const npyv_lanetype_
pun_hi.to_
)); \
} \ // 函数结束
NPY_FINLINE npyv_
(const npyv_lanetype_
{ \
return npyv_reinterpret_
(const npyv_lanetype_
)); \
} \ // 函数结束
NPY_FINLINE npyv_
(const npyv_lanetype_
{ \
return npyv_reinterpret_
(const npyv_lanetype_
)); \
} \ // 函数结束
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_
);
}
// 定义一个宏,用于实现 NEON SIMD 操作的加载和存储,用于两个通道的情况
// 定义一个内联函数,用于加载两个通道的 NEON 数据
NPY_FINLINE npyv_
const npyv_lanetype_
) { \
// 使用 NEON 指令 vld2q_
return vld2q_
} \
// 定义一个内联函数,用于存储两个通道的 NEON 数据
NPY_FINLINE void npyv_store_
npyv_lanetype_
) { \
// 使用 NEON 指令 vst2q_
vst2q_
}
// 定义 NEON SIMD 操作的函数模板,用于加载和存储 64 位数据的双向交错操作
NPY_FINLINE npyv_
const npyv_lanetype_
{ \
// 加载指针指向的前两个数据作为第一组向量 a 和第二组向量 b
npyv_
npyv_
// 创建存储两个向量的结构体 r
npyv_
// 将向量 a 和 b 的低位和高位组合成 r 的两个元素
r.val[0] = vcombine_
r.val[1] = vcombine_
// 返回结构体 r,包含了交错后的数据
return r; \
} \
// 定义存储函数,将交错后的数据存储回内存中
NPY_FINLINE void npyv_store_
npyv_lanetype_
{ \
// 将结构体 v 的第一组和第二组数据分别存储到指针指向的地址和下一个地址
npyv_store_
vget_low_
npyv_store_
vget_high_
}
// 实例化宏,生成具体的函数定义和实现
NPYV_IMPL_NEON_MEM_INTERLEAVE_64(u64)
NPYV_IMPL_NEON_MEM_INTERLEAVE_64(s64)
/*********************************
* Lookup table
*********************************/
// 使用矢量作为表中的索引
// 该表包含32个uint32元素。
NPY_FINLINE npyv_u32 npyv_lut32_u32(const npy_uint32 *table, npyv_u32 idx)
{
// 提取索引中的第一个值
const unsigned i0 = vgetq_lane_u32(idx, 0);
// 提取索引中的第二个值
const unsigned i1 = vgetq_lane_u32(idx, 1);
// 提取索引中的第三个值
const unsigned i2 = vgetq_lane_u32(idx, 2);
// 提取索引中的第四个值
const unsigned i3 = vgetq_lane_u32(idx, 3);
// 创建一个包含table[i0]的低位uint32x2_t值
uint32x2_t low = vcreate_u32(table[i0]);
// 从table中以i1为索引读取一个uint32_t值并将它加载到low的第二个位置
low = vld1_lane_u32((const uint32_t*)table + i1, low, 1);
// 创建一个包含table[i2]的高位uint32x2_t值
uint32x2_t high = vcreate_u32(table[i2]);
// 从table中以i3为索引读取一个uint32_t值并将它加载到high的第二个位置
high = vld1_lane_u32((const uint32_t*)table + i3, high, 1);
return vcombine_u32(low, high); // 组合低位和高位,返回结果
}
// 对npy_int32类型的表进行32位查找
NPY_FINLINE npyv_s32 npyv_lut32_s32(const npy_int32 *table, npyv_u32 idx)
{ return npyv_reinterpret_s32_u32(npyv_lut32_u32((const npy_uint32*)table, idx)); }
// 对float类型的表进行32位查找
NPY_FINLINE npyv_f32 npyv_lut32_f32(const float *table, npyv_u32 idx)
{ return npyv_reinterpret_f32_u32(npyv_lut32_u32((const npy_uint32*)table, idx)); }
// 使用矢量作为索引的表
// 该表包含16个uint64元素。
NPY_FINLINE npyv_u64 npyv_lut16_u64(const npy_uint64 *table, npyv_u64 idx)
{
// 从索引中提取低位值
const unsigned i0 = vgetq_lane_u32(vreinterpretq_u32_u64(idx), 0);
// 从索引中提取高位值
const unsigned i1 = vgetq_lane_u32(vreinterpretq_u32_u64(idx), 2);
return vcombine_u64(
vld1_u64((const uint64_t*)table + i0), // 从table中以i0为索引读取uint64_t值
vld1_u64((const uint64_t*)table + i1) // 从table中以i1为索引读取uint64_t值
);
}
// 对npy_int64类型的表进行64位查找
NPY_FINLINE npyv_s64 npyv_lut16_s64(const npy_int64 *table, npyv_u64 idx)
{ return npyv_reinterpret_s64_u64(npyv_lut16_u64((const npy_uint64*)table, idx)); }
// 当NPY_SIMD_F64宏被定义时,对double类型的表进行64位查找
NPY_FINLINE npyv_f64 npyv_lut16_f64(const double *table, npyv_u64 idx)
{ return npyv_reinterpret_f64_u64(npyv_lut16_u64((const npy_uint64*)table, idx)); }
.\numpy\numpy\_core\src\common\simd\neon\misc.h
// 如果未定义 NPY_SIMD 宏,则报错,因为此文件不可作为独立头文件使用
// 定义一个函数-like 宏,用于返回所有通道为零的 uint8x16_t 向量
// 定义一个函数-like 宏,用于返回所有通道为零的 int8x16_t 向量
// 定义一个函数-like 宏,用于返回所有通道为零的 uint16x8_t 向量
// 定义一个函数-like 宏,用于返回所有通道为零的 int16x8_t 向量
// 定义一个函数-like 宏,用于返回所有通道为零的 uint32x4_t 向量
// 定义一个函数-like 宏,用于返回所有通道为零的 int32x4_t 向量
// 定义一个函数-like 宏,用于返回所有通道为零的 uint64x2_t 向量
// 定义一个函数-like 宏,用于返回所有通道为零的 int64x2_t 向量
// 定义一个函数-like 宏,用于返回所有通道为零的 float32x4_t 向量
// 定义一个函数-like 宏,用于返回所有通道为零的 float64x2_t 向量
// 定义一个函数-like 宏,用于返回所有通道设置为指定值的 uint8x16_t 向量
// 定义一个函数-like 宏,用于返回所有通道设置为指定值的 int8x16_t 向量
// 定义一个函数-like 宏,用于返回所有通道设置为指定值的 uint16x8_t 向量
// 定义一个函数-like 宏,用于返回所有通道设置为指定值的 int16x8_t 向量
// 定义一个函数-like 宏,用于返回所有通道设置为指定值的 uint32x4_t 向量
// 定义一个函数-like 宏,用于返回所有通道设置为指定值的 int32x4_t 向量
// 定义一个函数-like 宏,用于返回所有通道设置为指定值的 uint64x2_t 向量
// 定义一个函数-like 宏,用于返回所有通道设置为指定值的 int64x2_t 向量
// 定义一个函数-like 宏,用于返回所有通道设置为指定值的 float32x4_t 向量
// 定义一个函数-like 宏,用于返回所有通道设置为指定值的 float64x2_t 向量
// 定义一个函数-like 宏,根据填充值 FILL 和其他参数,返回指定类型和值的向量
// 这些宏适用于不同大小的数据类型,并且具有变长参数列表
// 对于不支持变长参数的编译器,定义一个函数,手动设置向量的每个通道值,并返回向量
NPY_FINLINE uint8x16_t npyv__set_u8(npy_uint8 i0, npy_uint8 i1, npy_uint8 i2, npy_uint8 i3,
npy_uint8 i4, npy_uint8 i5, npy_uint8 i6, npy_uint8 i7, npy_uint8 i8, npy_uint8 i9,
npy_uint8 i10, npy_uint8 i11, npy_uint8 i12, npy_uint8 i13, npy_uint8 i14, npy_uint8 i15)
{
// 创建一个数据数组,存储指定的每个通道的值
const uint8_t NPY_DECL_ALIGNED(16) data[16] = {
i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15
};
// 使用 vld1q_u8 函数加载数据数组,返回一个 uint8x16_t 向量
return vld1q_u8(data);
}
NPY_FINLINE int8x16_t npyv__set_s8(npy_int8 i0, npy_int8 i1, npy_int8 i2, npy_int8 i3,
npy_int8 i4, npy_int8 i5, npy_int8 i6, npy_int8 i7, npy_int8 i8, npy_int8 i9,
npy_int8 i10, npy_int8 i11, npy_int8 i12, npy_int8 i13, npy_int8 i14, npy_int8 i15)
{
const int8_t NPY_DECL_ALIGNED(16) data[16] = {
i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15
};
return vld1q_s8(data);
}
NPY_FINLINE uint16x8_t npyv__set_u16(npy_uint16 i0, npy_uint16 i1, npy_uint16 i2, npy_uint16 i3,
npy_uint16 i4, npy_uint16 i5, npy_uint16 i6, npy_uint16 i7)
{
const uint16_t NPY_DECL_ALIGNED(16) data[8] = {i0, i1, i2, i3, i4, i5, i6, i7};
return vld1q_u16(data);
}
NPY_FINLINE int16x8_t npyv__set_s16(npy_int16 i0, npy_int16 i1, npy_int16 i2, npy_int16 i3,
npy_int16 i4, npy_int16 i5, npy_int16 i6, npy_int16 i7)
{
const int16_t NPY_DECL_ALIGNED(16) data[8] = {i0, i1, i2, i3, i4, i5, i6, i7};
return vld1q_s16(data);
}
NPY_FINLINE uint32x4_t npyv__set_u32(npy_uint32 i0, npy_uint32 i1, npy_uint32 i2, npy_uint32 i3)
{
const uint32_t NPY_DECL_ALIGNED(16) data[4] = {i0, i1, i2, i3};
return vld1q_u32(data);
}
NPY_FINLINE int32x4_t npyv__set_s32(npy_int32 i0, npy_int32 i1, npy_int32 i2, npy_int32 i3)
{
const int32_t NPY_DECL_ALIGNED(16) data[4] = {i0, i1, i2, i3};
return vld1q_s32(data);
}
NPY_FINLINE uint64x2_t npyv__set_u64(npy_uint64 i0, npy_uint64 i1)
{
const uint64_t NPY_DECL_ALIGNED(16) data[2] = {i0, i1};
return vld1q_u64(data);
}
NPY_FINLINE int64x2_t npyv__set_s64(npy_int64 i0, npy_int64 i1)
{
const int64_t NPY_DECL_ALIGNED(16) data[2] = {i0, i1};
return vld1q_s64(data);
}
NPY_FINLINE float32x4_t npyv__set_f32(float i0, float i1, float i2, float i3)
{
const float NPY_DECL_ALIGNED(16) data[4] = {i0, i1, i2, i3};
return vld1q_f32(data);
}
NPY_FINLINE float64x2_t npyv__set_f64(double i0, double i1)
{
const double NPY_DECL_ALIGNED(16) data[2] = {i0, i1};
return vld1q_f64(data);
}
// 宏定义:设置每个向量的特定值,并将其余的所有向量设为零
// 宏定义:按每个向量的每个元素选择
// 宏定义:提取第一个向量的第一个元素
// 宏定义:重新解释数据类型为另一种数据类型
// 定义一系列宏,用于将不同类型的数据重新解释为 float32 向量
// 定义一个宏,用于将相同类型的 float64 数据重新解释为 float64 向量
// AVX2/AVX512 架构下才需要执行的清理操作宏
.\numpy\numpy\_core\src\common\simd\neon\neon.h
// 定义 SIMD 宽度为 128 bits
// 定义 SIMD 宽度为 16 bytes
// 定义支持单精度浮点数操作
// 如果是 ARM64 架构,定义支持双精度浮点数操作
// 否则,不支持双精度浮点数操作
// 如果支持 NEON 指令集的 FMA3 指令,设置为原生支持
// 否则,使用硬件模拟的方式支持 FMA3 指令
// 定义 SIMD 架构为小端模式
// 定义 SIMD 比较信号的支持
// 下面是各种数据类型的 SIMD 向量定义
typedef uint8x16_t npyv_u8;
typedef int8x16_t npyv_s8;
typedef uint16x8_t npyv_u16;
typedef int16x8_t npyv_s16;
typedef uint32x4_t npyv_u32;
typedef int32x4_t npyv_s32;
typedef uint64x2_t npyv_u64;
typedef int64x2_t npyv_s64;
typedef float32x4_t npyv_f32;
typedef float64x2_t npyv_f64;
typedef uint8x16_t npyv_b8;
typedef uint16x8_t npyv_b16;
typedef uint32x4_t npyv_b32;
typedef uint64x2_t npyv_b64;
// 各种 SIMD 向量的多重结构定义
typedef uint8x16x2_t npyv_u8x2;
typedef int8x16x2_t npyv_s8x2;
typedef uint16x8x2_t npyv_u16x2;
typedef int16x8x2_t npyv_s16x2;
typedef uint32x4x2_t npyv_u32x2;
typedef int32x4x2_t npyv_s32x2;
typedef uint64x2x2_t npyv_u64x2;
typedef int64x2x2_t npyv_s64x2;
typedef float32x4x2_t npyv_f32x2;
typedef float64x2x2_t npyv_f64x2;
typedef uint8x16x3_t npyv_u8x3;
typedef int8x16x3_t npyv_s8x3;
typedef uint16x8x3_t npyv_u16x3;
typedef int16x8x3_t npyv_s16x3;
typedef uint32x4x3_t npyv_u32x3;
typedef int32x4x3_t npyv_s32x3;
typedef uint64x2x3_t npyv_u64x3;
typedef int64x2x3_t npyv_s64x3;
typedef float32x4x3_t npyv_f32x3;
typedef float64x2x3_t npyv_f64x3;
// 各种数据类型 SIMD 向量的通道数定义
// 包含 SIMD 相关的头文件
.\numpy\numpy\_core\src\common\simd\neon\operators.h
/***************************
* Shifting
***************************/
// left shift operations for various data types
// left shift operations by an immediate constant for various data types
// right shift operations for various data types
// right shift operations by an immediate constant for various data types
/***************************
* Logical
***************************/
// AND operations for various data types
vreinterpretq_f32_u8(vandq_u8(vreinterpretq_u8_f32(A), vreinterpretq_u8_f32(B)))
vreinterpretq_f64_u8(vandq_u8(vreinterpretq_u8_f64(A), vreinterpretq_u8_f64(B)))
// OR operations for various data types
vreinterpretq_f32_u8(vorrq_u8(vreinterpretq_u8_f32(A), vreinterpretq_u8_f32(B)))
vreinterpretq_f64_u8(vorrq_u8(vreinterpretq_u8_f64(A), vreinterpretq_u8_f64(B)))
// XOR operations for various data types
vreinterpretq_f32_u8(veorq_u8(vreinterpretq_u8_f32(A), vreinterpretq_u8_f32(B)))
vreinterpretq_f64_u8(veorq_u8(vreinterpretq_u8_f64(A), vreinterpretq_u8_f64(B)))
vreinterpretq_f32_u8(veorq_u8(vreinterpretq_u8_f32(A), vreinterpretq_u8_f32(B)))
// 定义宏 npyv_xor_f64,实现两个双精度浮点向量的按位异或操作
vreinterpretq_f64_u8(veorq_u8(vreinterpretq_u8_f64(A), vreinterpretq_u8_f64(B)))
// 定义宏 npyv_xor_b8 到 npyv_xor_b64,分别实现8位、16位、32位和64位整数向量的按位异或操作
// 定义宏 npyv_not_u8 到 npyv_not_b64,分别实现8位、16位、32位和64位的按位取反操作
// 定义宏 npyv_andc_u8 和 npyv_andc_b8,实现8位整数向量的按位与补操作
// 定义宏 npyv_orc_b8,实现8位整数向量的按位或补操作
// 定义宏 npyv_xnor_b8,实现8位整数向量的按位异或非操作
/***************************
* Comparison
***************************/
// 定义宏 npyv_cmpeq_u8 到 npyv_cmpeq_f64,分别实现相等比较操作
// 在 ARM 64位 架构下定义宏 npyv_cmpeq_u64 和 npyv_cmpeq_s64,实现64位整数向量的相等比较操作
// 在其他架构下定义函数 npyv_cmpeq_u64,实现64位整数向量的相等比较操作
NPY_FINLINE uint64x2_t npyv_cmpeq_u64(uint64x2_t a, uint64x2_t b)
{
// 将64位整数向量转换为32位整数向量进行比较,再将结果转换回64位整数向量
uint64x2_t cmpeq = vreinterpretq_u64_u32(vceqq_u32(
vreinterpretq_u32_u64(a), vreinterpretq_u32_u64(b)
));
uint64x2_t cmpeq_h = vshlq_n_u64(cmpeq, 32); // 左移32位
uint64x2_t test = vandq_u64(cmpeq, cmpeq_h); // 与操作
return vreinterpretq_u64_s64(vshrq_n_s64(vreinterpretq_s64_u64(test), 32)); // 右移32位
}
npyv_cmpeq_u64(vreinterpretq_u64_s64(A), vreinterpretq_u64_s64(B)) // 实现64位有符号整数向量的相等比较操作
// 定义宏 npyv_cmpneq_u8 到 npyv_cmpneq_f64,分别实现不等比较操作
// 定义宏 npyv_cmpgt_u8 到 npyv_cmpgt_f64,分别实现大于比较操作
// 在 ARM 64位 架构下定义宏 npyv_cmpgt_u64 和 npyv_cmpgt_s64,实现64位整数向量的大于比较操作
NPY_FINLINE uint64x2_t npyv_cmpgt_s64(int64x2_t a, int64x2_t b)
{
int64x2_t sub = vsubq_s64(b, a);
uint64x2_t nsame_sbit = vreinterpretq_u64_s64(veorq_s64(a, b));
int64x2_t test = vbslq_s64(nsame_sbit, b, sub);
int64x2_t extend_sbit = vshrq_n_s64(test, 63);
return vreinterpretq_u64_s64(extend_sbit);
}
NPY_FINLINE uint64x2_t npyv_cmpgt_u64(uint64x2_t a, uint64x2_t b)
{
const uint64x2_t sbit = npyv_setall_u64(0x8000000000000000);
a = npyv_xor_u64(a, sbit);
b = npyv_xor_u64(b, sbit);
return npyv_cmpgt_s64(vreinterpretq_s64_u64(a), vreinterpretq_s64_u64(b));
}
// 定义比较操作:大于等于
// 小于比较
// 小于等于比较
// 检查特殊情况
NPY_FINLINE npyv_b32 npyv_notnan_f32(npyv_f32 a)
{
/**
* 为了避免信号qNaN,对于clang对称输入的错误
* 检查 https://github.com/numpy/numpy/issues/22933,
* 以获取更多的解释
*/
npyv_b32 ret;
__asm("fcmeq %0.4s, %1.4s, %1.4s" : "=w" (ret) : "w" (a));
__asm("vceq.f32 %q0, %q1, %q1" : "=w" (ret) : "w" (a));
return ret;
return vceqq_f32(a, a);
}
NPY_FINLINE npyv_b64 npyv_notnan_f64(npyv_f64 a)
{
npyv_b64 ret;
__asm("fcmeq %0.2d, %1.2d, %1.2d" : "=w" (ret) : "w" (a));
return ret;
return vceqq_f64(a, a);
}
// 测试跨所有向量通道
// any: 如果任何元素不等于零则返回true
// all: 如果所有元素都不等于零则返回true
NPY_FINLINE bool npyv_any_b
{ return vmaxvq_u
NPY_FINLINE bool npyv_all_b
{ return vminvq_u
NPYV_IMPL_NEON_ANYALL(8) // 检查8个元素中是否有任何一个不等于零
NPYV_IMPL_NEON_ANYALL(16) // 检查16个元素中是否有任何一个不等于零
NPYV_IMPL_NEON_ANYALL(32) // 检查32个元素中是否有任何一个不等于零
// 定义 NEON SIMD 操作的任意元素和所有元素的宏函数
NPY_FINLINE bool npyv_any_
// 实现检查 NEON SIMD 向量是否存在非零元素的函数
{ return npyv_any_
NPY_FINLINE bool npyv_all_
// 实现检查 NEON SIMD 向量是否所有元素都非零的函数
{ return npyv_all_
NPYV_IMPL_NEON_ANYALL(u8, u8, b8)
NPYV_IMPL_NEON_ANYALL(s8, u8, b8)
NPYV_IMPL_NEON_ANYALL(u16, u16, b16)
NPYV_IMPL_NEON_ANYALL(s16, u16, b16)
NPYV_IMPL_NEON_ANYALL(u32, u32, b32)
NPYV_IMPL_NEON_ANYALL(s32, u32, b32)
NPY_FINLINE bool npyv_any_b64(npyv_b64 a)
// 实现检查 NEON SIMD 向量是否存在非零 64 位元素的函数
{ return vmaxvq_u32(vreinterpretq_u32_u64(a)) != 0; }
NPY_FINLINE bool npyv_all_b64(npyv_b64 a)
// 实现检查 NEON SIMD 向量是否所有 64 位元素都非零的函数
{ return vminvq_u32(vreinterpretq_u32_u64(a)) != 0; }
NPY_FINLINE bool npyv_all_u64(npyv_u64 a)
{
uint32x4_t a32 = vreinterpretq_u32_u64(a);
a32 = vorrq_u32(a32, vrev64q_u32(a32));
// 使用 NEON 指令将向量的 64 位数据转换为 32 位并反转,以便检查是否所有元素非零
return vminvq_u32(a32) != 0;
}
NPY_FINLINE bool npyv_any_s64(npyv_s64 a)
// 实现检查 NEON SIMD 向量是否存在非零 64 位有符号整数元素的函数
{ return npyv_any_u64(vreinterpretq_u64_s64(a)); }
NPY_FINLINE bool npyv_all_s64(npyv_s64 a)
// 实现检查 NEON SIMD 向量是否所有 64 位有符号整数元素都非零的函数
{ return npyv_all_u64(vreinterpretq_u64_s64(a)); }
// 定义 NEON SIMD 操作的任意元素和所有元素的宏函数,特定于浮点类型
NPY_FINLINE bool npyv_any_
// 实现检查 NEON SIMD 向量是否存在非零元素的函数,特定于浮点类型
{ return !npyv_all_
NPY_FINLINE bool npyv_all_
// 实现检查 NEON SIMD 向量是否所有元素都非零的函数,特定于浮点类型
{ return !npyv_any_
NPYV_IMPL_NEON_ANYALL(f32, b32)
NPYV_IMPL_NEON_ANYALL(f64, b64)
NPY_FINLINE bool npyv_any_b
{ \
// 将布尔型向量转换为64位整型向量
int64x2_t a64 = vreinterpretq_s64_u
// 检查向量中是否有任何一个元素为真
return ( \
vgetq_lane_s64(a64, 0) | \
vgetq_lane_s64(a64, 1) \
) != 0; \
} \
NPY_FINLINE bool npyv_all_b
{ \
// 将布尔型向量转换为64位整型向量
int64x2_t a64 = vreinterpretq_s64_u
// 检查向量中所有元素是否都为真
return ( \
vgetq_lane_s64(a64, 0) & \
vgetq_lane_s64(a64, 1) \
) == -1; \
}
// 实现NEON指令集的任意/全部操作,针对不同长度的布尔型向量
NPYV_IMPL_NEON_ANYALL(8)
NPYV_IMPL_NEON_ANYALL(16)
NPYV_IMPL_NEON_ANYALL(32)
NPYV_IMPL_NEON_ANYALL(64)
NPY_FINLINE bool npyv_any_
{ \
// 将SFX类型向量转换为64位整型向量
int64x2_t a64 = vreinterpretq_s64_
// 检查向量中是否有任何一个元素为真
return ( \
vgetq_lane_s64(a64, 0) | \
vgetq_lane_s64(a64, 1) \
) != 0; \
} \
NPY_FINLINE bool npyv_all_
{ \
// 比较SFX类型向量与零向量,检查是否所有元素都为零
npyv_
a, npyv_zero_
); \
// 将USFX类型向量转换为64位整型向量
int64x2_t a64 = vreinterpretq_s64_
// 检查向量中所有元素是否都为真
return ( \
vgetq_lane_s64(a64, 0) & \
vgetq_lane_s64(a64, 1) \
) == 0; \
}
// 实现NEON指令集的任意/全部操作,包括无符号和有符号不同类型
NPYV_IMPL_NEON_ANYALL(u8, u8)
NPYV_IMPL_NEON_ANYALL(s8, u8)
NPYV_IMPL_NEON_ANYALL(u16, u16)
NPYV_IMPL_NEON_ANYALL(s16, u16)
NPYV_IMPL_NEON_ANYALL(u32, u32)
NPYV_IMPL_NEON_ANYALL(s32, u32)
// 检查32位浮点型向量中是否有任何一个元素为真
NPY_FINLINE bool npyv_any_f32(npyv_f32 a)
{
uint32x4_t tz = npyv_cmpeq_f32(a, npyv_zero_f32());
// 将32位浮点型向量转换为64位整型向量,并检查是否有任何一个元素为真
int64x2_t a64 = vreinterpretq_s64_u32(tz);
return (vgetq_lane_s64(a64, 0) & vgetq_lane_s64(a64, 1)) != -1ll;
}
// 检查32位浮点型向量中是否所有元素都为真
NPY_FINLINE bool npyv_all_f32(npyv_f32 a)
{
// 将浮点向量 a 中每个元素与零进行比较,生成一个比较结果向量 tz
uint32x4_t tz = npyv_cmpeq_f32(a, npyv_zero_f32());
// 将结果向量 tz 转换为含有两个 64 位有符号整数的向量 a64
int64x2_t a64 = vreinterpretq_s64_u32(tz);
// 检查 a64 中的两个元素是否都为零,返回检查结果
return (vgetq_lane_s64(a64, 0) | vgetq_lane_s64(a64, 1)) == 0;
}
NPY_FINLINE bool npyv_any_s64(npyv_s64 a)
{
// 检查输入向量 a 中的两个 64 位有符号整数元素是否有任意一个非零,返回检查结果
return (vgetq_lane_s64(a, 0) | vgetq_lane_s64(a, 1)) != 0;
}
NPY_FINLINE bool npyv_all_s64(npyv_s64 a)
{
// 检查输入向量 a 中的两个 64 位有符号整数元素是否都非零,返回检查结果
return vgetq_lane_s64(a, 0) && vgetq_lane_s64(a, 1);
}
NPY_FINLINE bool npyv_any_u64(npyv_u64 a)
{
// 检查输入向量 a 中的两个 64 位无符号整数元素是否有任意一个非零,返回检查结果
return (vgetq_lane_u64(a, 0) | vgetq_lane_u64(a, 1)) != 0;
}
NPY_FINLINE bool npyv_all_u64(npyv_u64 a)
{
// 检查输入向量 a 中的两个 64 位无符号整数元素是否都非零,返回检查结果
return vgetq_lane_u64(a, 0) && vgetq_lane_u64(a, 1);
}
// 如果定义了 NPY_SIMD_F64 宏,则结束当前的条件编译段落
// 结束当前的条件编译段落,该段落由 _NPY_SIMD_NEON_OPERATORS_H 宏控制
.\numpy\numpy\_core\src\common\simd\neon\reorder.h
// 定义 __aarch64__ 情况下的向量操作宏,用于合并两个向量的低部分元素
// 定义非 __aarch64__ 情况下的向量操作宏,用于合并两个向量的低部分元素
// 定义 __aarch64__ 情况下的向量操作宏,用于合并两个向量的高部分元素
// 定义非 __aarch64__ 情况下的向量操作宏,用于合并两个向量的高部分元素
// 定义宏函数 NPYV_IMPL_NEON_COMBINE,用于将两个给定类型 T_VEC 的向量合并成一个类型为 T_VEC
// 内联函数,将两个类型为 T_VEC 的向量 a 和 b 合并
NPY_FINLINE T_VEC
{ \
// 创建类型为 T_VEC
r.val[0] = NPY_CAT(npyv_combinel_, SFX)(a, b); \
// val[1] 是通过 npyv_combineh_SFX 函数合并 a 和 b 的结果
r.val[1] = NPY_CAT(npyv_combineh_, SFX)(a, b); \
// 返回合并后的结果结构体 r
return r; \
}
// 根据给定类型和后缀 SFX 实例化 NPYV_IMPL_NEON_COMBINE 宏
NPYV_IMPL_NEON_COMBINE(npyv_u8, u8)
NPYV_IMPL_NEON_COMBINE(npyv_s8, s8)
NPYV_IMPL_NEON_COMBINE(npyv_u16, u16)
NPYV_IMPL_NEON_COMBINE(npyv_s16, s16)
NPYV_IMPL_NEON_COMBINE(npyv_u32, u32)
NPYV_IMPL_NEON_COMBINE(npyv_s32, s32)
NPYV_IMPL_NEON_COMBINE(npyv_u64, u64)
NPYV_IMPL_NEON_COMBINE(npyv_s64, s64)
NPYV_IMPL_NEON_COMBINE(npyv_f32, f32)
// 根据宏 __aarch64__ 的定义条件编译以下部分代码块
// 定义宏函数 NPYV_IMPL_NEON_ZIP,用于对给定类型 T_VEC 的向量执行交织(interleave)和解交织(deinterleave)操作
// 内联函数,将两个类型为 T_VEC 的向量 a 和 b 进行交织操作
NPY_FINLINE T_VEC
{ \
// 创建类型为 T_VEC
r.val[0] = vzip1q_
// val[1] 是通过 vzip2q_SFX(a, b) 进行的交织操作
r.val[1] = vzip2q_
// 返回交织后的结果结构体 r
return r; \
} \
// 内联函数,将两个类型为 T_VEC 的向量 a 和 b 进行解交织操作
NPY_FINLINE T_VEC
{ \
// 创建类型为 T_VEC
r.val[0] = vuzp1q_
// val[1] 是通过 vuzp2q_SFX(a, b) 进行的解交织操作
r.val[1] = vuzp2q_
// 返回解交织后的结果结构体 r
return r; \
}
// 若未定义 __aarch64__ 宏,则编译以下代码块
// 定义宏函数 NPYV_IMPL_NEON_ZIP,用于对给定类型 T_VEC 的向量执行交织(interleave)和解交织(deinterleave)操作
// 内联函数,将两个类型为 T_VEC 的向量 a 和 b 进行交织操作
NPY_FINLINE T_VEC
{ return vzipq_
// 内联函数,将两个类型为 T_VEC 的向量 a 和 b 进行解交织操作
NPY_FINLINE T_VEC
{ return vuzpq_
// 根据给定类型和后缀 SFX 实例化 NPYV_IMPL_NEON_ZIP 宏
NPYV_IMPL_NEON_ZIP(npyv_u8, u8)
NPYV_IMPL_NEON_ZIP(npyv_s8, s8)
NPYV_IMPL_NEON_ZIP(npyv_u16, u16)
NPYV_IMPL_NEON_ZIP(npyv_s16, s16)
NPYV_IMPL_NEON_ZIP(npyv_u32, u32)
NPYV_IMPL_NEON_ZIP(npyv_s32, s32)
NPYV_IMPL_NEON_ZIP(npyv_f32, f32)
// 定义一系列宏函数,将不同类型的向量直接映射到其对应的合并和解交织函数
// 定义一系列宏函数,用于反转每个 64 位通道中的元素顺序
// 定义宏 npyv_rev64_f32 用于反转 NEON 寄存器中 64 位浮点数元素的顺序
// 根据不同的编译器预处理指令,定义宏 npyv_permi128_u32,用于对 NEON 寄存器中的 128 位整数型数据进行按指定索引重新排列
__builtin_shufflevector(A, A, E0, E1, E2, E3)
// 对于 Clang 编译器,使用 __builtin_shufflevector 进行元素重新排列
__builtin_shuffle(A, npyv_set_u32(E0, E1, E2, E3))
// 对于 GCC 编译器,使用 __builtin_shuffle 结合 npyv_set_u32 宏进行元素重新排列
npyv_set_u32( \
vgetq_lane_u32(A, E0), vgetq_lane_u32(A, E1), \
vgetq_lane_u32(A, E2), vgetq_lane_u32(A, E3) \
)
// 对于其他编译器,使用 vgetq_lane_u32 获取指定索引处的 32 位整数,并通过 npyv_set_u32 宏进行元素重新排列
npyv_set_s32( \
vgetq_lane_s32(A, E0), vgetq_lane_s32(A, E1), \
vgetq_lane_s32(A, E2), vgetq_lane_s32(A, E3) \
)
npyv_set_f32( \
vgetq_lane_f32(A, E0), vgetq_lane_f32(A, E1), \
vgetq_lane_f32(A, E2), vgetq_lane_f32(A, E3) \
)
// 对于 Clang 和 GCC 编译器,定义整数和浮点数的 128 位元素重排宏
// 根据不同的编译器预处理指令,定义宏 npyv_permi128_u64,用于对 NEON 寄存器中的 128 位无符号长整型数据进行按指定索引重新排列
__builtin_shufflevector(A, A, E0, E1)
// 对于 Clang 编译器,使用 __builtin_shufflevector 进行元素重新排列
__builtin_shuffle(A, npyv_set_u64(E0, E1))
// 对于 GCC 编译器,使用 __builtin_shuffle 结合 npyv_set_u64 宏进行元素重新排列
npyv_set_u64( \
vgetq_lane_u64(A, E0), vgetq_lane_u64(A, E1) \
)
npyv_set_s64( \
vgetq_lane_s64(A, E0), vgetq_lane_s64(A, E1) \
)
npyv_set_f64( \
vgetq_lane_f64(A, E0), vgetq_lane_f64(A, E1) \
)
// 对于 Clang 和 GCC 编译器,定义整数和浮点数的 128 位元素重排宏
// 如果不支持双精度浮点数 SIMD 操作,则取消定义 npyv_permi128_f64 宏
// 结束 _NPY_SIMD_NEON_REORDER_H 文件的条件编译
.\numpy\numpy\_core\src\common\simd\simd.h
/**
* the NumPy C SIMD vectorization interface "NPYV" are types and functions intended
* to simplify vectorization of code on different platforms, currently supports
* the following SIMD extensions SSE, AVX2, AVX512, VSX and NEON.
*
* TODO: Add an independent sphinx doc.
*/
extern "C" {
/*
* clang commit an aggressive optimization behaviour when flag `-ftrapping-math`
* isn't fully supported that's present at -O1 or greater. When partially loading a
* vector register for a operations that requires to fill up the remaining lanes
* with certain value for example divide operation needs to fill the remaining value
* with non-zero integer to avoid fp exception divide-by-zero.
* clang optimizer notices that the entire register is not needed for the store
* and optimizes out the fill of non-zero integer to the remaining
* elements. As workaround we mark the returned register with `volatile`
* followed by symmetric operand operation e.g. `or`
* to convince the compiler that the entire vector is needed.
*/
/*
* Avoid using any of the following intrinsics with MSVC 32-bit,
* even if they are apparently work on newer versions.
* They had bad impact on the generated instructions,
* sometimes the compiler deal with them without the respect
* of 32-bit mode which lead to crush due to execute 64-bit
* instructions and other times generate bad emulated instructions.
*/
// lane type by intrin suffix
typedef npy_uint8 npyv_lanetype_u8; // 定义无符号 8 位整数的 SIMD 向量元素类型
typedef npy_int8 npyv_lanetype_s8; // 定义有符号 8 位整数的 SIMD 向量元素类型
typedef npy_uint16 npyv_lanetype_u16; // 定义无符号 16 位整数的 SIMD 向量元素类型
typedef npy_int16 npyv_lanetype_s16; // 定义有符号 16 位整数的 SIMD 向量元素类型
typedef npy_uint32 npyv_lanetype_u32; // 定义无符号 32 位整数的 SIMD 向量元素类型
typedef npy_int32 npyv_lanetype_s32; // 定义有符号 32 位整数的 SIMD 向量元素类型
typedef npy_uint64 npyv_lanetype_u64; // 定义无符号 64 位整数的 SIMD 向量元素类型
typedef npy_int64 npyv_lanetype_s64; // 定义有符号 64 位整数的 SIMD 向量元素类型
typedef float npyv_lanetype_f32; // 定义单精度浮点数的 SIMD 向量元素类型
typedef double npyv_lanetype_f64; // 定义双精度浮点数的 SIMD 向量元素类型
// TODO: Add support for VSX(2.06) and BE Mode for VSX
/// 定义:如果没有可用的SIMD扩展,NPY_SIMD为0,否则为SIMD的位宽(以比特为单位)。
/// 定义:如果没有可用的SIMD扩展,NPY_SIMD_WIDTH为0,否则为SIMD的位宽(以字节为单位)。
/// 定义:如果启用的SIMD扩展支持单精度浮点数,则为1,否则为0。
/// 定义:如果启用的SIMD扩展支持双精度浮点数,则为1,否则为0。
/// 定义:如果启用的SIMD扩展支持本地FMA(Fused Multiply-Add)操作,则为1,否则为0。
/// 注意:即使不支持FMA指令集,仍然会模拟(快速)FMA操作,但在精度要求高时不应使用。
/// 定义:如果启用的SIMD扩展在大端模式下运行,则为1,否则为0。
/// 定义:如果支持的比较指令集(lt, le, gt, ge)在处理静默NaN时引发浮点无效异常,则为1,否则为0。
// 如果 _NPY_SIMD_H_ 未定义,则执行以下代码,防止头文件重复包含
// 结束条件,确保头文件在多次包含时不会被重复定义
// 如果未定义 NPY_HAVE_AVX512F 并且 NPY_SIMD 为真且小于 512,则包含 emulate_maskop.h 头文件
// 如果 NPY_SIMD 为真,则包含 intdiv.h 头文件
/**
* Some SIMD extensions currently(AVX2, AVX512F) require (de facto)
* a maximum number of strides sizes when dealing with non-contiguous memory access.
*
* Therefore the following functions must be used to check the maximum
* acceptable limit of strides before using any of non-contiguous load/store intrinsics.
*
* For instance:
* npy_intp ld_stride = step[0] / sizeof(float);
* npy_intp st_stride = step[1] / sizeof(float);
*
* if (npyv_loadable_stride_f32(ld_stride) && npyv_storable_stride_f32(st_stride)) {
* for (;;)
* npyv_f32 a = npyv_loadn_f32(ld_pointer, ld_stride);
* // ...
* npyv_storen_f32(st_pointer, st_stride, a);
* }
* else {
* for (;;)
* // C scalars
* }
*/
// 以下是一段注释,描述了一些 SIMD 扩展(如 AVX2, AVX512F)在处理非连续内存访问时的最大步长限制,以及使用非连续加载/存储指令前必须使用的函数。
// 如果未定义这些宏,则定义它们并初始化为 0,用于限制 SIMD 加载和存储的最大步长
NPY_FINLINE int npyv_loadable_stride_
{ return MAXLOAD > 0 ? llabs(stride) <= MAXLOAD : 1; } \
NPY_FINLINE int npyv_storable_stride_
{ return MAXSTORE > 0 ? llabs(stride) <= MAXSTORE : 1; }
// 定义一个宏,生成两个内联函数,用于检查给定步长是否在指定的最大加载或存储步长范围内
NPYV_IMPL_MAXSTRIDE(u32, NPY_SIMD_MAXLOAD_STRIDE32, NPY_SIMD_MAXSTORE_STRIDE32)
NPYV_IMPL_MAXSTRIDE(s32, NPY_SIMD_MAXLOAD_STRIDE32, NPY_SIMD_MAXSTORE_STRIDE32)
NPYV_IMPL_MAXSTRIDE(f32, NPY_SIMD_MAXLOAD_STRIDE32, NPY_SIMD_MAXSTORE_STRIDE32)
NPYV_IMPL_MAXSTRIDE(u64, NPY_SIMD_MAXLOAD_STRIDE64, NPY_SIMD_MAXSTORE_STRIDE64)
NPYV_IMPL_MAXSTRIDE(s64, NPY_SIMD_MAXLOAD_STRIDE64, NPY_SIMD_MAXSTORE_STRIDE64)
// 如果 NPY_SIMD 为真,则实例化各种数据类型(u32, s32, f32, u64, s64)的加载和存储步长检查函数
NPYV_IMPL_MAXSTRIDE(f64, NPY_SIMD_MAXLOAD_STRIDE64, NPY_SIMD_MAXSTORE_STRIDE64)
// 如果支持双精度 SIMD(NPY_SIMD_F64 为真),则实例化 f64 类型的加载和存储步长检查函数
}
// 如果是 C++ 环境,则关闭外部 C 链接,并结束头文件保护符 `
.\numpy\numpy\_core\src\common\simd\simd_utils.h
// 定义用于创建长度为2的向量的宏,CAST是类型转换宏,I0和I1是初始值,__VA_ARGS__是可变参数列表
// 定义用于创建长度为4的向量的宏,CAST是类型转换宏,I0到I3是初始值,__VA_ARGS__是可变参数列表
(CAST)(I0), (CAST)(I1), (CAST)(I2), (CAST)(I3)
// 定义用于创建长度为8的向量的宏,CAST是类型转换宏,I0到I7是初始值,__VA_ARGS__是可变参数列表
(CAST)(I0), (CAST)(I1), (CAST)(I2), (CAST)(I3), (CAST)(I4), (CAST)(I5), (CAST)(I6), (CAST)(I7)
// 定义用于创建长度为16的向量的宏,CAST是类型转换宏,I0到I15是初始值,__VA_ARGS__是可变参数列表
NPYV__SET_8(CAST, I0, I1, I2, I3, I4, I5, I6, I7), \
NPYV__SET_8(CAST, I8, I9, I10, I11, I12, I13, I14, I15)
// 定义用于创建长度为32的向量的宏,CAST是类型转换宏,I0到I31是初始值,__VA_ARGS__是可变参数列表
I16, I17, I18, I19, I20, I21, I22, I23, I24, I25, I26, I27, I28, I29, I30, I31, ...) \
\
NPYV__SET_16(CAST, I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15), \
NPYV__SET_16(CAST, I16, I17, I18, I19, I20, I21, I22, I23, I24, I25, I26, I27, I28, I29, I30, I31)
// 定义用于创建长度为64的向量的宏,CAST是类型转换宏,I0到I63是初始值,__VA_ARGS__是可变参数列表
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, ...) \
\
NPYV__SET_32(CAST, 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), \
NPYV__SET_32(CAST, 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)
// 定义用于创建长度为2的向量的宏,CAST是类型转换宏,F是填充值,__VA_ARGS__是可变参数列表
// 定义用于创建长度为4的向量的宏,CAST是类型转换宏,F是填充值,__VA_ARGS__是可变参数列表
// 定义用于创建长度为8的向量的宏,CAST是类型转换宏,F是填充值,__VA_ARGS__是可变参数列表
// 定义用于创建长度为16的向量的宏,CAST是类型转换宏,F是填充值,__VA_ARGS__是可变参数列表
F, F, F, F, F, F, F, F, F, F, F, F, F, F, F))
// 定义用于创建长度为32的向量的宏,CAST是类型转换宏,F是填充值,__VA_ARGS__是可变参数列表
F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F))
// 定义用于创建长度为64的向量的宏,CAST是类型转换宏,F是填充值,__VA_ARGS__是可变参数列表
F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, \
F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F))
.\numpy\numpy\_core\src\common\simd\sse\arithmetic.h
/***************************
* Addition
***************************/
// 非饱和加法
// 饱和加法
// TODO: 实现 Packs intrins 后继续添加
/***************************
* Subtraction
***************************/
// 非饱和减法
// 饱和减法
// TODO: 实现 Packs intrins 后继续添加
/***************************
* Multiplication
***************************/
// 非饱和乘法(8位无符号整数特化)
NPY_FINLINE __m128i npyv_mul_u8(__m128i a, __m128i b)
{
// 构造掩码,用于选择乘法结果的偶数位置字节
const __m128i mask = _mm_set1_epi32(0xFF00FF00);
// 偶数位置乘法结果
__m128i even = _mm_mullo_epi16(a, b);
// 奇数位置乘法结果
__m128i odd = _mm_mullo_epi16(_mm_srai_epi16(a, 8), _mm_srai_epi16(b, 8));
odd = _mm_slli_epi16(odd, 8);
// 选择最终结果
return npyv_select_u8(mask, odd, even);
}
// 32位无符号整数乘法(未实现 SSE4.1 的情况下)
NPY_FINLINE __m128i npyv_mul_u32(__m128i a, __m128i b)
{
// 偶数位置乘法结果
__m128i even = _mm_mul_epu32(a, b);
// 奇数位置乘法结果
__m128i odd = _mm_mul_epu32(_mm_srli_epi64(a, 32), _mm_srli_epi64(b, 32));
// 合并低32位和高32位结果
__m128i low = _mm_unpacklo_epi32(even, odd);
__m128i high = _mm_unpackhi_epi32(even, odd);
return _mm_unpacklo_epi64(low, high);
}
// TODO: 模拟64位整数乘法
// 饱和乘法
// TODO: 实现 Packs intrins 后继续添加
/***************************
* Integer Division
***************************/
// 参见 simd/intdiv.h 以获取更多说明
// 将每个无符号8位元素除以预计算的除数
NPY_FINLINE npyv_u8 npyv_divc_u8(npyv_u8 a, const npyv_u8x3 divisor)
{
// 构造掩码,用于选择除法结果的低位字节
const __m128i bmask = _mm_set1_epi32(0x00FF00FF);
// 根据除数的第二个元素设置移位量
const __m128i shf1b = _mm_set1_epi8(0xFFU >> _mm_cvtsi128_si32(divisor.val[1]));
// 设置 shf2b 为一个包含 divisor.val[2] 的低 8 位的掩码
const __m128i shf2b = _mm_set1_epi8(0xFFU >> _mm_cvtsi128_si32(divisor.val[2]));
// 计算偶数位置的高位乘积:a 与 bmask 按位与,再与 divisor.val[0] 相乘
__m128i mulhi_even = _mm_mullo_epi16(_mm_and_si128(a, bmask), divisor.val[0]);
// 计算奇数位置的高位乘积:a 右移 8 位,再与 divisor.val[0] 相乘
__m128i mulhi_odd = _mm_mullo_epi16(_mm_srli_epi16(a, 8), divisor.val[0]);
// 将偶数和奇数位置的高位乘积结果右移 8 位
mulhi_even = _mm_srli_epi16(mulhi_even, 8);
// 使用 bmask 选择偶数位置或奇数位置的高位乘积结果
__m128i mulhi = npyv_select_u8(bmask, mulhi_even, mulhi_odd);
// 计算 floor(a/d) = (mulhi + ((a - mulhi) >> sh1)) >> sh2
__m128i q = _mm_sub_epi8(a, mulhi); // a - mulhi
q = _mm_and_si128(_mm_srl_epi16(q, divisor.val[1]), shf1b); // (a - mulhi) >> sh1
q = _mm_add_epi8(mulhi, q); // mulhi + ((a - mulhi) >> sh1)
q = _mm_and_si128(_mm_srl_epi16(q, divisor.val[2]), shf2b); // ((mulhi + ((a - mulhi) >> sh1)) >> sh2)
// 返回计算结果 q
return q;
// 结束 numpPy 内联函数 npyv_divc_s8,将每个有符号 8 位元素除以预先计算的除数(向零舍入)
NPY_FINLINE npyv_s8 npyv_divc_s8(npyv_s8 a, const npyv_s8x3 divisor)
{
// 创建一个掩码,用于选择每个 8 位元素的低位部分
const __m128i bmask = _mm_set1_epi32(0x00FF00FF);
// 使用 npyv_divc_s16 函数分别计算偶数和奇数位元素的除法
__m128i divc_even = npyv_divc_s16(_mm_srai_epi16(_mm_slli_epi16(a, 8), 8), divisor);
__m128i divc_odd = npyv_divc_s16(_mm_srai_epi16(a, 8), divisor);
// 将奇数位元素左移 8 位,再与偶数位元素结合,形成结果
divc_odd = _mm_slli_epi16(divc_odd, 8);
return npyv_select_u8(bmask, divc_even, divc_odd);
}
// 按照预先计算的除数,将每个无符号 16 位元素进行除法
NPY_FINLINE npyv_u16 npyv_divc_u16(npyv_u16 a, const npyv_u16x3 divisor)
{
// 使用无符号乘法的高位部分
__m128i mulhi = _mm_mulhi_epu16(a, divisor.val[0]);
// floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2
__m128i q = _mm_sub_epi16(a, mulhi);
q = _mm_srl_epi16(q, divisor.val[1]);
q = _mm_add_epi16(mulhi, q);
q = _mm_srl_epi16(q, divisor.val[2]);
return q;
}
// 按照预先计算的除数,将每个有符号 16 位元素进行除法(向零舍入)
NPY_FINLINE npyv_s16 npyv_divc_s16(npyv_s16 a, const npyv_s16x3 divisor)
{
// 使用有符号乘法的高位部分
__m128i mulhi = _mm_mulhi_epi16(a, divisor.val[0]);
// q = ((a + mulhi) >> sh1) - XSIGN(a)
// trunc(a/d) = (q ^ dsign) - dsign
__m128i q = _mm_sra_epi16(_mm_add_epi16(a, mulhi), divisor.val[1]);
q = _mm_sub_epi16(q, _mm_srai_epi16(a, 15));
q = _mm_sub_epi16(_mm_xor_si128(q, divisor.val[2]), divisor.val[2]);
return q;
}
// 按照预先计算的除数,将每个无符号 32 位元素进行除法
NPY_FINLINE npyv_u32 npyv_divc_u32(npyv_u32 a, const npyv_u32x3 divisor)
{
// 使用无符号乘法的高位部分,分别计算偶数和奇数位元素的乘积
__m128i mulhi_even = _mm_srli_epi64(_mm_mul_epu32(a, divisor.val[0]), 32);
__m128i mulhi_odd = _mm_mul_epu32(_mm_srli_epi64(a, 32), divisor.val[0]);
// 使用 SSE4.1 提供的指令混合偶数和奇数位元素的乘积
__m128i mulhi = _mm_blend_epi16(mulhi_even, mulhi_odd, 0xCC);
// 使用非 SSE4.1 的方式混合偶数和奇数位元素的乘积
__m128i mask_13 = _mm_setr_epi32(0, -1, 0, -1);
mulhi_odd = _mm_and_si128(mulhi_odd, mask_13);
__m128i mulhi = _mm_or_si128(mulhi_even, mulhi_odd);
// floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2
__m128i q = _mm_sub_epi32(a, mulhi);
q = _mm_srl_epi32(q, divisor.val[1]);
q = _mm_add_epi32(mulhi, q);
q = _mm_srl_epi32(q, divisor.val[2]);
return q;
}
// 按照预先计算的除数,将每个有符号 32 位元素进行除法(向零舍入)
NPY_FINLINE npyv_s32 npyv_divc_s32(npyv_s32 a, const npyv_s32x3 divisor)
{
// 取每个有符号 32 位元素的符号
__m128i asign = _mm_srai_epi32(a, 31);
// 使用 SSE4.1 提供的指令,计算有符号乘法的高位部分
__m128i mulhi_even = _mm_srli_epi64(_mm_mul_epi32(a, divisor.val[0]), 32);
__m128i mulhi_odd = _mm_mul_epi32(_mm_srli_epi64(a, 32), divisor.val[0]);
__m128i mulhi = _mm_blend_epi16(mulhi_even, mulhi_odd, 0xCC);
// 如果不支持 SSE4.1,执行以下代码段
// 计算无符号乘法的高位部分
__m128i mulhi_even = _mm_srli_epi64(_mm_mul_epu32(a, divisor.val[0]), 32);
// 计算偶数索引位置的乘法高位
__m128i mulhi_odd = _mm_mul_epu32(_mm_srli_epi64(a, 32), divisor.val[0]);
// 创建掩码以选择奇数索引位置的乘法高位结果
__m128i mask_13 = _mm_setr_epi32(0, -1, 0, -1);
mulhi_odd = _mm_and_si128(mulhi_odd, mask_13);
// 合并偶数和奇数索引位置的乘法高位结果
__m128i mulhi = _mm_or_si128(mulhi_even, mulhi_odd);
// 将无符号乘法结果转换为带符号的高位乘法
// mulhi - ((a < 0) ? m : 0) - ((m < 0) ? a : 0);
const __m128i msign= _mm_srai_epi32(divisor.val[0], 31);
// 对 msign 和 a 进行按位与操作
__m128i m_asign = _mm_and_si128(divisor.val[0], asign);
__m128i a_msign = _mm_and_si128(a, msign);
mulhi = _mm_sub_epi32(mulhi, m_asign);
mulhi = _mm_sub_epi32(mulhi, a_msign);
// 计算商 q = ((a + mulhi) >> sh1) - XSIGN(a)
__m128i q = _mm_sra_epi32(_mm_add_epi32(a, mulhi), divisor.val[1]);
q = _mm_sub_epi32(q, asign);
q = _mm_sub_epi32(_mm_xor_si128(q, divisor.val[2]), divisor.val[2]);
return q;
}
// 返回无符号 64 位乘法的高 64 位结果
// 参考:https://stackoverflow.com/a/28827013
NPY_FINLINE npyv_u64 npyv__mullhi_u64(npyv_u64 a, npyv_u64 b)
{
__m128i lomask = npyv_setall_s64(0xffffffff);
// 将 a 向右移动 32 位,得到高位部分
__m128i a_hi = _mm_srli_epi64(a, 32); // a0l, a0h, a1l, a1h
// 将 b 向右移动 32 位,得到高位部分
__m128i b_hi = _mm_srli_epi64(b, 32); // b0l, b0h, b1l, b1h
// 计算部分乘积
__m128i w0 = _mm_mul_epu32(a, b); // a0l*b0l, a1l*b1l
__m128i w1 = _mm_mul_epu32(a, b_hi); // a0l*b0h, a1l*b1h
__m128i w2 = _mm_mul_epu32(a_hi, b); // a0h*b0l, a1h*b0l
__m128i w3 = _mm_mul_epu32(a_hi, b_hi); // a0h*b0h, a1h*b1h
// 求和部分乘积
__m128i w0h = _mm_srli_epi64(w0, 32);
__m128i s1 = _mm_add_epi64(w1, w0h);
__m128i s1l = _mm_and_si128(s1, lomask);
__m128i s1h = _mm_srli_epi64(s1, 32);
__m128i s2 = _mm_add_epi64(w2, s1l);
__m128i s2h = _mm_srli_epi64(s2, 32);
__m128i hi = _mm_add_epi64(w3, s1h);
hi = _mm_add_epi64(hi, s2h);
return hi;
}
// 每个无符号 64 位元素除以预先计算的除数
NPY_FINLINE npyv_u64 npyv_divc_u64(npyv_u64 a, const npyv_u64x3 divisor)
{
// 计算无符号乘法的高位部分
__m128i mulhi = npyv__mullhi_u64(a, divisor.val[0]);
// floor(a/d) = (mulhi + ((a-mulhi) >> sh1)) >> sh2
__m128i q = _mm_sub_epi64(a, mulhi);
q = _mm_srl_epi64(q, divisor.val[1]);
q = _mm_add_epi64(mulhi, q);
q = _mm_srl_epi64(q, divisor.val[2]);
return q;
}
// 每个有符号 64 位元素除以预先计算的除数(向零舍入)
NPY_FINLINE npyv_s64 npyv_divc_s64(npyv_s64 a, const npyv_s64x3 divisor)
{
// 计算无符号乘法的高位部分
__m128i mulhi = npyv__mullhi_u64(a, divisor.val[0]);
// 使用函数 npyv__mullhi_u64 对 a 和 divisor.val[0] 进行无符号64位乘法,并将结果存储在 mulhi 中
// 这一步计算得到的是 a 与 divisor.val[0] 的乘积的高64位部分
// convert unsigned to signed high multiplication
// 将无符号乘法转换为有符号乘法的高位运算
// mulhi - ((a < 0) ? m : 0) - ((m < 0) ? a : 0);
// 如果 a 小于 0,则将 m 加到 mulhi 上;如果 m 小于 0,则将 a 加到 mulhi 上。
// 这段代码可能用于在无符号乘法的基础上进行有符号调整,确保乘法结果在有符号整数环境下的正确性。
// 如果支持 SSE4.2,则使用比较运算寄存器来设置除数的符号位
const __m128i msign= _mm_cmpgt_epi64(_mm_setzero_si128(), divisor.val[0]);
// 使用比较运算寄存器来设置被除数 a 的符号位
__m128i asign = _mm_cmpgt_epi64(_mm_setzero_si128(), a);
// 如果不支持 SSE4.2,则通过移位和 shuffle 操作设置除数的符号位
const __m128i msign= _mm_srai_epi32(_mm_shuffle_epi32(divisor.val[0], _MM_SHUFFLE(3, 3, 1, 1)), 31);
// 通过移位和 shuffle 操作设置被除数 a 的符号位
__m128i asign = _mm_srai_epi32(_mm_shuffle_epi32(a, _MM_SHUFFLE(3, 3, 1, 1)), 31);
// 计算除数和被除数符号位的按位与结果
__m128i m_asign = _mm_and_si128(divisor.val[0], asign);
// 计算被除数和除数符号位的按位与结果
__m128i a_msign = _mm_and_si128(a, msign);
// 对 mulhi 执行符号位修正
mulhi = _mm_sub_epi64(mulhi, m_asign);
mulhi = _mm_sub_epi64(mulhi, a_msign);
// 计算商 q,即 (a + mulhi) >> sh,其中 sh 是移位参数
__m128i q = _mm_add_epi64(a, mulhi);
// 模拟算术右移操作
const __m128i sigb = npyv_setall_s64(1LL << 63);
q = _mm_srl_epi64(_mm_add_epi64(q, sigb), divisor.val[1]);
q = _mm_sub_epi64(q, _mm_srl_epi64(sigb, divisor.val[1]));
// 修正商 q,即 q = q - XSIGN(a)
q = _mm_sub_epi64(q, asign);
// 执行截断操作,即 trunc(a/d) = (q ^ dsign) - dsign
q = _mm_sub_epi64(_mm_xor_si128(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
// multiply and add, a*b + c
// multiply and subtract, a*b - c
// negate multiply and add, -(a*b) + c
// multiply, add for odd elements and subtract even elements.
// (a * b) -+ c
// 如果不支持 FMA 指令集,则定义一些函数来模拟乘加和乘减操作
// multiply and add, a*b + c
NPY_FINLINE npyv_f32 npyv_muladd_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c)
{ return npyv_add_f32(npyv_mul_f32(a, b), c); }
NPY_FINLINE npyv_f64 npyv_muladd_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c)
{ return npyv_add_f64(npyv_mul_f64(a, b), c); }
// 定义一个内联函数,实现浮点数向量 a 和 b 的乘法,再减去向量 c 的结果
NPY_FINLINE npyv_f32 npyv_mulsub_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c)
{ return npyv_sub_f32(npyv_mul_f32(a, b), c); }
// 定义一个内联函数,实现双精度浮点数向量 a 和 b 的乘法,再减去向量 c 的结果
NPY_FINLINE npyv_f64 npyv_mulsub_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c)
{ return npyv_sub_f64(npyv_mul_f64(a, b), c); }
// 定义一个内联函数,实现浮点数向量 a 和 b 的乘法的相反数,再加上向量 c 的结果
NPY_FINLINE npyv_f32 npyv_nmuladd_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c)
{ return npyv_sub_f32(c, npyv_mul_f32(a, b)); }
// 定义一个内联函数,实现双精度浮点数向量 a 和 b 的乘法的相反数,再加上向量 c 的结果
NPY_FINLINE npyv_f64 npyv_nmuladd_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c)
{ return npyv_sub_f64(c, npyv_mul_f64(a, b)); }
// 定义一个内联函数,实现浮点数向量 a 和 b 的乘法结果加上向量 c 的结果(奇数元素加,偶数元素减)
// (a * b) -+ c
NPY_FINLINE npyv_f32 npyv_muladdsub_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c)
{
// 计算 a 和 b 的乘积
npyv_f32 m = npyv_mul_f32(a, b);
// 如果支持 SSE3 指令集,则使用 SSE3 中的加减操作
return _mm_addsub_ps(m, c);
// 如果不支持 SSE3,则手动实现加减操作
const npyv_f32 msign = npyv_set_f32(-0.0f, 0.0f, -0.0f, 0.0f);
return npyv_add_f32(m, npyv_xor_f32(msign, c));
}
// 定义一个内联函数,实现双精度浮点数向量 a 和 b 的乘法结果加上向量 c 的结果(奇数元素加,偶数元素减)
NPY_FINLINE npyv_f64 npyv_muladdsub_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c)
{
// 计算 a 和 b 的乘积
npyv_f64 m = npyv_mul_f64(a, b);
// 如果支持 SSE3 指令集,则使用 SSE3 中的加减操作
return _mm_addsub_pd(m, c);
// 如果不支持 SSE3,则手动实现加减操作
const npyv_f64 msign = npyv_set_f64(-0.0, 0.0);
return npyv_add_f64(m, npyv_xor_f64(msign, c));
}
// negate multiply and subtract, -(a*b) - c
// 定义一个内联函数,用于实现 -(a*b) - c 的操作
NPY_FINLINE npyv_f32 npyv_nmulsub_f32(npyv_f32 a, npyv_f32 b, npyv_f32 c)
{
// 计算 a 的负数
npyv_f32 neg_a = npyv_xor_f32(a, npyv_setall_f32(-0.0f));
// 返回 -(a*b) - c 的结果
return npyv_sub_f32(npyv_mul_f32(neg_a, b), c);
}
// 定义一个内联函数,用于实现 -(a*b) - c 的操作,针对双精度浮点数
NPY_FINLINE npyv_f64 npyv_nmulsub_f64(npyv_f64 a, npyv_f64 b, npyv_f64 c)
{
// 计算 a 的负数
npyv_f64 neg_a = npyv_xor_f64(a, npyv_setall_f64(-0.0));
// 返回 -(a*b) - c 的结果
return npyv_sub_f64(npyv_mul_f64(neg_a, b), c);
}
/***************************
* Summation
***************************/
// reduce sum across vector
// 对无符号 32 位整数向量进行求和
NPY_FINLINE npy_uint32 npyv_sum_u32(npyv_u32 a)
{
// 将向量 a 按元素相加,并将结果保存在临时变量 t 中
__m128i t = _mm_add_epi32(a, _mm_srli_si128(a, 8));
t = _mm_add_epi32(t, _mm_srli_si128(t, 4));
// 将临时变量 t 的最低元素转换为 unsigned int 类型并返回
return (unsigned)_mm_cvtsi128_si32(t);
}
// 对无符号 64 位整数向量进行求和
NPY_FINLINE npy_uint64 npyv_sum_u64(npyv_u64 a)
{
// 将向量 a 的前后两个元素按位相加,并将结果保存在临时变量 one 中
__m128i one = _mm_add_epi64(a, _mm_unpackhi_epi64(a, a));
// 将临时变量 one 的元素转换为 unsigned long long 类型并返回
return (npy_uint64)npyv128_cvtsi128_si64(one);
}
// 对单精度浮点数向量进行求和
NPY_FINLINE float npyv_sum_f32(npyv_f32 a)
{
// 使用 SSE3 指令集实现单精度浮点数向量的求和
__m128 sum_halves = _mm_hadd_ps(a, a);
return _mm_cvtss_f32(_mm_hadd_ps(sum_halves, sum_halves));
// 使用传统 SSE 指令集实现单精度浮点数向量的求和
__m128 t1 = _mm_movehl_ps(a, a);
__m128 t2 = _mm_add_ps(a, t1);
__m128 t3 = _mm_shuffle_ps(t2, t2, 1);
__m128 t4 = _mm_add_ss(t2, t3);
return _mm_cvtss_f32(t4);
}
// 对双精度浮点数向量进行求和
NPY_FINLINE double npyv_sum_f64(npyv_f64 a)
{
// 使用 SSE3 指令集实现双精度浮点数向量的求和
return _mm_cvtsd_f64(_mm_hadd_pd(a, a));
// 使用传统 SSE 指令集实现双精度浮点数向量的求和
return _mm_cvtsd_f64(_mm_add_pd(a, _mm_unpackhi_pd(a, a)));
}
// expand the source vector and performs sum reduce
// 对无符号 8 位整数向量进行求和
NPY_FINLINE npy_uint16 npyv_sumup_u8(npyv_u8 a)
{
// 将向量 a 的所有元素累加到一个 16 位整数向量 two 中
__m128i two = _mm_sad_epu8(a, _mm_setzero_si128());
// 将向量 two 的前后两个元素按位相加,并将结果保存在临时变量 one 中
__m128i one = _mm_add_epi16(two, _mm_unpackhi_epi64(two, two));
// 将临时变量 one 的元素转换为 unsigned short 类型并返回
return (npy_uint16)_mm_cvtsi128_si32(one);
}
// 对无符号 16 位整数向量进行求和
NPY_FINLINE npy_uint32 npyv_sumup_u16(npyv_u16 a)
{
// 创建一个掩码,用于提取 a 中的偶数位元素
const __m128i even_mask = _mm_set1_epi32(0x0000FFFF);
// 将 a 中的偶数位元素提取到向量 even 中
__m128i even = _mm_and_si128(a, even_mask);
// 将 a 中的奇数位元素向右移动 16 位,并与 even 相加得到 four
__m128i odd = _mm_srli_epi32(a, 16);
__m128i four = _mm_add_epi32(even, odd);
// 调用 npyv_sum_u32 函数对 four 进行求和,并返回结果
return npyv_sum_u32(four);
}
.\numpy\numpy\_core\src\common\simd\sse\conversion.h
// 将掩码类型转换为整数类型
// 将整数类型转换为掩码类型
// 将布尔向量转换为整数位字段
NPY_FINLINE npy_uint64 npyv_tobits_b8(npyv_b8 a)
{ return (npy_uint16)_mm_movemask_epi8(a); }
NPY_FINLINE npy_uint64 npyv_tobits_b16(npyv_b16 a)
{
__m128i pack = _mm_packs_epi16(a, a);
return (npy_uint8)_mm_movemask_epi8(pack);
}
NPY_FINLINE npy_uint64 npyv_tobits_b32(npyv_b32 a)
{ return (npy_uint8)_mm_movemask_ps(_mm_castsi128_ps(a)); }
NPY_FINLINE npy_uint64 npyv_tobits_b64(npyv_b64 a)
{ return (npy_uint8)_mm_movemask_pd(_mm_castsi128_pd(a)); }
// 扩展操作
NPY_FINLINE npyv_u16x2 npyv_expand_u16_u8(npyv_u8 data) {
npyv_u16x2 r;
const __m128i z = _mm_setzero_si128();
r.val[0] = _mm_unpacklo_epi8(data, z);
r.val[1] = _mm_unpackhi_epi8(data, z);
return r;
}
NPY_FINLINE npyv_u32x2 npyv_expand_u32_u16(npyv_u16 data) {
npyv_u32x2 r;
const __m128i z = _mm_setzero_si128();
r.val[0] = _mm_unpacklo_epi16(data, z);
r.val[1] = _mm_unpackhi_epi16(data, z);
return r;
}
// 将两个16位布尔向量打包成一个8位布尔向量
NPY_FINLINE npyv_b8 npyv_pack_b8_b16(npyv_b16 a, npyv_b16 b) {
return _mm_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) {
npyv_b16 ab = _mm_packs_epi32(a, b);
npyv_b16 cd = _mm_packs_epi32(c, d);
return npyv_pack_b8_b16(ab, cd);
}
// 将八个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) {
npyv_b32 ab = _mm_packs_epi32(a, b);
npyv_b32 cd = _mm_packs_epi32(c, d);
npyv_b32 ef = _mm_packs_epi32(e, f);
npyv_b32 gh = _mm_packs_epi32(g, h);
return npyv_pack_b8_b32(ab, cd, ef, gh);
}
// 将单精度浮点数向量四舍五入到最近的整数(假设偶数)
NPY_FINLINE npyv_s32 npyv_round_s32_f64(npyv_f64 a, npyv_f64 b)
{
__m128i lo = _mm_cvtpd_epi32(a), hi = _mm_cvtpd_epi32(b);
return _mm_unpacklo_epi64(lo, hi);
}
.\numpy\numpy\_core\src\common\simd\sse\math.h
/***************************
* Elementary
***************************/
// 平方根函数定义
// 倒数函数定义
NPY_FINLINE npyv_f32 npyv_recip_f32(npyv_f32 a)
{ return _mm_div_ps(_mm_set1_ps(1.0f), a); }
NPY_FINLINE npyv_f64 npyv_recip_f64(npyv_f64 a)
{ return _mm_div_pd(_mm_set1_pd(1.0), a); }
// 绝对值函数定义
NPY_FINLINE npyv_f32 npyv_abs_f32(npyv_f32 a)
{
return _mm_and_ps(
a, _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff))
);
}
NPY_FINLINE npyv_f64 npyv_abs_f64(npyv_f64 a)
{
return _mm_and_pd(
a, _mm_castsi128_pd(npyv_setall_s64(0x7fffffffffffffffLL))
);
}
// 平方函数定义
NPY_FINLINE npyv_f32 npyv_square_f32(npyv_f32 a)
{ return _mm_mul_ps(a, a); }
NPY_FINLINE npyv_f64 npyv_square_f64(npyv_f64 a)
{ return _mm_mul_pd(a, a); }
// 最大值函数定义,直接映射,不保证处理 NaN
// 最大值函数,支持 IEEE 浮点算术(IEC 60559)
// - 如果其中一个向量包含 NaN,则设置另一个向量对应元素
// - 只有当两个对应元素都是 NaN 时,才设置 NaN
NPY_FINLINE npyv_f32 npyv_maxp_f32(npyv_f32 a, npyv_f32 b)
{
__m128i nn = npyv_notnan_f32(b);
__m128 max = _mm_max_ps(a, b);
return npyv_select_f32(nn, max, a);
}
NPY_FINLINE npyv_f64 npyv_maxp_f64(npyv_f64 a, npyv_f64 b)
{
__m128i nn = npyv_notnan_f64(b);
__m128d max = _mm_max_pd(a, b);
return npyv_select_f64(nn, max, a);
}
NPY_FINLINE npyv_f32 npyv_maxn_f32(npyv_f32 a, npyv_f32 b)
{
__m128i nn = npyv_notnan_f32(a);
__m128 max = _mm_max_ps(a, b);
return npyv_select_f32(nn, max, a);
}
NPY_FINLINE npyv_f64 npyv_maxn_f64(npyv_f64 a, npyv_f64 b)
{
__m128i nn = npyv_notnan_f64(a);
__m128d max = _mm_max_pd(a, b);
return npyv_select_f64(nn, max, a);
}
// 最大值函数,整数操作
NPY_FINLINE npyv_s8 npyv_max_s8(npyv_s8 a, npyv_s8 b)
{
return npyv_select_s8(npyv_cmpgt_s8(a, b), a, b);
}
NPY_FINLINE npyv_u16 npyv_max_u16(npyv_u16 a, npyv_u16 b)
{
return npyv_select_u16(npyv_cmpgt_u16(a, b), a, b);
}
NPY_FINLINE npyv_u32 npyv_max_u32(npyv_u32 a, npyv_u32 b)
{
return npyv_select_u32(npyv_cmpgt_u32(a, b), a, b);
}
NPY_FINLINE npyv_s32 npyv_max_s32(npyv_s32 a, npyv_s32 b)
{
return npyv_select_s32(npyv_cmpgt_s32(a, b), a, b);
}
NPY_FINLINE npyv_u64 npyv_max_u64(npyv_u64 a, npyv_u64 b)
{
return npyv_select_u64(npyv_cmpgt_u64(a, b), a, b);
}
NPY_FINLINE npyv_s64 npyv_max_s64(npyv_s64 a, npyv_s64 b)
{
return npyv_select_s64(npyv_cmpgt_s64(a, b), a, b);
// Minimum, natively mapping with no guarantees to handle NaN.
// 定义了几个宏用于执行单精度和双精度浮点数的最小值操作
// Minimum, supports IEEE floating-point arithmetic (IEC 60559),
// - If one of the two vectors contains NaN, the equivalent element of the other vector is set
// - Only if both corresponded elements are NaN, NaN is set.
// 实现了对单精度浮点数向量的最小值操作,支持 IEEE 浮点算术(IEC 60559),
// - 如果两个向量中的一个包含 NaN,则设置另一个向量对应元素的值
// - 只有当两个对应的元素都是 NaN 时,才会设置为 NaN
NPY_FINLINE npyv_f32 npyv_minp_f32(npyv_f32 a, npyv_f32 b)
{
// 使用 nopyv_notnan_f32 函数获取非 NaN 的掩码
__m128i nn = npyv_notnan_f32(b);
// 使用 SSE 指令计算 a 和 b 向量中的最小值
__m128 min = _mm_min_ps(a, b);
// 根据非 NaN 的掩码选择最小值或原始值来创建新的向量
return npyv_select_f32(nn, min, a);
}
// 类似于上述函数,实现了对双精度浮点数向量的最小值操作
NPY_FINLINE npyv_f64 npyv_minp_f64(npyv_f64 a, npyv_f64 b)
{
__m128i nn = npyv_notnan_f64(b);
__m128d min = _mm_min_pd(a, b);
return npyv_select_f64(nn, min, a);
}
// 实现了对单精度浮点数向量的最小值操作
NPY_FINLINE npyv_f32 npyv_minn_f32(npyv_f32 a, npyv_f32 b)
{
__m128i nn = npyv_notnan_f32(a);
__m128 min = _mm_min_ps(a, b);
return npyv_select_f32(nn, min, a);
}
// 实现了对双精度浮点数向量的最小值操作
NPY_FINLINE npyv_f64 npyv_minn_f64(npyv_f64 a, npyv_f64 b)
{
__m128i nn = npyv_notnan_f64(a);
__m128d min = _mm_min_pd(a, b);
return npyv_select_f64(nn, min, a);
}
// Minimum, integer operations
// 使用 SSE4.1 指令集提供的最小值操作宏
// 对于不支持 SSE4.1 的平台,实现了对应整数类型的最小值操作
NPY_FINLINE npyv_s8 npyv_min_s8(npyv_s8 a, npyv_s8 b)
{
// 使用条件选择函数实现 s8 类型向量的最小值操作
return npyv_select_s8(npyv_cmplt_s8(a, b), a, b);
}
NPY_FINLINE npyv_u16 npyv_min_u16(npyv_u16 a, npyv_u16 b)
{
// 使用条件选择函数实现 u16 类型向量的最小值操作
return npyv_select_u16(npyv_cmplt_u16(a, b), a, b);
}
NPY_FINLINE npyv_u32 npyv_min_u32(npyv_u32 a, npyv_u32 b)
{
// 使用条件选择函数实现 u32 类型向量的最小值操作
return npyv_select_u32(npyv_cmplt_u32(a, b), a, b);
}
NPY_FINLINE npyv_s32 npyv_min_s32(npyv_s32 a, npyv_s32 b)
{
// 使用条件选择函数实现 s32 类型向量的最小值操作
return npyv_select_s32(npyv_cmplt_s32(a, b), a, b);
}
// 使用 SSE2 指令集提供的最小值操作宏
// 实现了对 u64 类型向量的最小值操作
NPY_FINLINE npyv_u64 npyv_min_u64(npyv_u64 a, npyv_u64 b)
{
// 使用条件选择函数实现 u64 类型向量的最小值操作
return npyv_select_u64(npyv_cmplt_u64(a, b), a, b);
}
// 实现了对 s64 类型向量的最小值操作
NPY_FINLINE npyv_s64 npyv_min_s64(npyv_s64 a, npyv_s64 b)
{
// 使用条件选择函数实现 s64 类型向量的最小值操作
return npyv_select_s64(npyv_cmplt_s64(a, b), a, b);
}
// reduce min&max for 32&64-bits
// 定义了宏用于实现对 32 位和 64 位数据类型的最小和最大值缩减操作
NPY_FINLINE STYPE
{ \
// 使用 SSE 指令集进行数据重排和比较,得到最小值
__m128i v64 = npyv_
// 再次重排和比较,得到最小值
__m128i v32 = npyv_
// 将结果转换为对应的 32 位整数并返回
return (STYPE
} \
NPY_FINLINE STYPE
{
__m128i v64 = npyv_
return (STYPE
}
这段代码中,使用了一些宏定义和 SIMD(单指令多数据流)指令,用于处理数据向量化操作,具体的 INTRIN 和 STYPE 是根据具体的上下文和宏定义来确定的。
// 定义宏 NPY_IMPL_SSE_REDUCE_MINMAX,用于生成不同类型和函数的 SSE 指令实现
NPY_IMPL_SSE_REDUCE_MINMAX(npy_uint, min_u, min_epu)
NPY_IMPL_SSE_REDUCE_MINMAX(npy_int, min_s, min_epi)
NPY_IMPL_SSE_REDUCE_MINMAX(npy_uint, max_u, max_epu)
NPY_IMPL_SSE_REDUCE_MINMAX(npy_int, max_s, max_epi)
// 取消宏定义 NPY_IMPL_SSE_REDUCE_MINMAX
// 宏重新定义 NPY_IMPL_SSE_REDUCE_MINMAX,用于生成 SSE 指令实现
// 定义单精度浮点数的 SSE 指令实现,用于计算最小值或最大值
NPY_FINLINE float npyv_reduce_
{ \
// 将向量 a 按指定方式重新排列,得到 64 位浮点数向量 v64
__m128 v64 = _mm_
// 将 v64 按指定方式重新排列,得到 32 位浮点数向量 v32
__m128 v32 = _mm_
// 返回 v32 的第一个元素作为浮点数结果
return _mm_cvtss_f32(v32); \
} \
// 定义双精度浮点数的 SSE 指令实现,用于计算最小值或最大值
NPY_FINLINE double npyv_reduce_
{ \
// 将向量 a 按指定方式重新排列,得到 64 位双精度浮点数向量 v64
__m128d v64 = _mm_
// 返回 v64 的第一个元素作为双精度浮点数结果
return _mm_cvtsd_f64(v64); \
} \
// 定义单精度浮点数的 SSE 指令实现,用于计算带有处理 NaN 的最小值或最大值
NPY_FINLINE float npyv_reduce_
{ \
// 获取 a 中非 NaN 的掩码
npyv_b32 notnan = npyv_notnan_f32(a); \
// 如果所有元素都是 NaN,则返回 a 的第一个元素
if (NPY_UNLIKELY(!npyv_any_b32(notnan))) { \
return _mm_cvtss_f32(a); \
} \
// 选取非 NaN 的元素或者用 INF 替换 NaN 的元素
a = npyv_select_f32(notnan, a, npyv_reinterpret_f32_u32(npyv_setall_u32(INF))); \
// 调用 npyv_reduce_
return npyv_reduce_
} \
// 定义双精度浮点数的 SSE 指令实现,用于计算带有处理 NaN 的最小值或最大值
NPY_FINLINE double npyv_reduce_
{
npyv_b64 notnan = npyv_notnan_f64(a);
if (NPY_UNLIKELY(!npyv_any_b64(notnan))) {
return _mm_cvtsd_f64(a);
}
a = npyv_select_f64(notnan, a, npyv_reinterpret_f64_u64(npyv_setall_u64(INF64)));
return npyv_reduce_
}
NPY_FINLINE float npyv_reduce_
{
npyv_b32 notnan = npyv_notnan_f32(a);
if (NPY_UNLIKELY(!npyv_all_b32(notnan))) {
const union { npy_uint32 i; float f;} pnan = {0x7fc00000UL};
return pnan.f;
}
return npyv_reduce_
}
NPY_FINLINE double npyv_reduce_
{
npyv_b64 notnan = npyv_notnan_f64(a);
if (NPY_UNLIKELY(!npyv_all_b64(notnan))) {
const union { npy_uint64 i; double d;} pnan = {0x7ff8000000000000ull};
return pnan.d;
}
return npyv_reduce_
}
// 定义 SSE 指令的宏,用于实现最小值和最大值的规约操作
// 定义内联函数,用于减少 SSE 寄存器中 16 位数据的最小值或最大值
NPY_FINLINE STYPE
{ \
// 将输入的 128 位整数按指定方式重组成 64 位整数
__m128i v64 = npyv_
// 进一步重组以得到 32 位整数
__m128i v32 = npyv_
// 最后得到 16 位整数,返回其结果
__m128i v16 = npyv_
return (STYPE
} \
// 定义内联函数,用于减少 SSE 寄存器中 8 位数据的最小值或最大值
NPY_FINLINE STYPE
{ \
// 将输入的 128 位整数按指定方式重组成 64 位整数
__m128i v64 = npyv_
// 进一步重组以得到 32 位整数
__m128i v32 = npyv_
// 再得到 16 位整数
__m128i v16 = npyv_
// 最后得到 8 位整数,通过逻辑右移实现除以 2
__m128i v8 = npyv_
return (STYPE
}
// 使用宏定义实现不同数据类型的最小值和最大值规约操作
NPY_IMPL_SSE_REDUCE_MINMAX(npy_uint, min_u)
NPY_IMPL_SSE_REDUCE_MINMAX(npy_int, min_s)
NPY_IMPL_SSE_REDUCE_MINMAX(npy_uint, max_u)
NPY_IMPL_SSE_REDUCE_MINMAX(npy_int, max_s)
// 如果支持 SSE4.1 指令集,则使用 SSE4.1 的指令执行浮点数四舍五入到最近偶数
NPY_FINLINE npyv_f32 npyv_rint_f32(npyv_f32 a)
{
// 使用 SSE4.1 的指令实现浮点数的四舍五入到最近偶数
return _mm_round_ps(a, _MM_FROUND_TO_NEAREST_INT);
// 如果不支持 SSE4.1 指令集,则使用以下代码实现浮点数的四舍五入到最近偶数
// 设置一个 32 位浮点数全为 -0.0f 的向量
const __m128 szero = _mm_set1_ps(-0.0f);
// 设置一个 32 位整数表示指数掩码为 0xff000000
const __m128i exp_mask = _mm_set1_epi32(0xff000000);
// 生成一个掩码,用于检测非无限值
__m128i nfinite_mask = _mm_slli_epi32(_mm_castps_si128(a), 1);
nfinite_mask = _mm_and_si128(nfinite_mask, exp_mask);
nfinite_mask = _mm_cmpeq_epi32(nfinite_mask, exp_mask);
// 消除 NaN 和 Inf,以避免无效的浮点错误
__m128 x = _mm_xor_ps(a, _mm_castsi128_ps(nfinite_mask));
// 将浮点数转换为整数进行四舍五入
__m128i roundi = _mm_cvtps_epi32(x);
// 将整数转换回浮点数
__m128 round = _mm_cvtepi32_ps(roundi);
// 处理带符号的零
round = _mm_or_ps(round, _mm_and_ps(a, szero));
// 如果溢出则返回原始值
__m128i overflow_mask = _mm_cmpeq_epi32(roundi, _mm_castps_si128(szero));
// 如果溢出或者非有限值,则返回原始值,否则返回四舍五入后的结果
return npyv_select_f32(_mm_or_si128(nfinite_mask, overflow_mask), a, round);
}
NPY_FINLINE npyv_f64 npyv_rint_f64(npyv_f64 a)
{
// 如果支持 SSE4.1 指令集,则使用 SSE 指令进行向最近整数舍入并返回结果
return _mm_round_pd(a, _MM_FROUND_TO_NEAREST_INT);
// 如果不支持 SSE4.1 指令集,则进行手动实现舍入操作
// 设置常量 -0.0 的向量
const __m128d szero = _mm_set1_pd(-0.0);
// 设置常量 2^52 的向量
const __m128d two_power_52 = _mm_set1_pd(0x10000000000000);
// 计算出 NaN 掩码,用于排除 NaN 值,避免在比较操作中出现无效的浮点错误
__m128d nan_mask = _mm_cmpunord_pd(a, a);
// 计算绝对值向量,并处理 NaN 值
__m128d abs_x = npyv_abs_f64(_mm_xor_pd(nan_mask, a));
// 执行舍入操作,加上魔法数 2^52
__m128d round = _mm_sub_pd(_mm_add_pd(two_power_52, abs_x), two_power_52);
// 复制原始值的符号到舍入结果中
round = _mm_or_pd(round, _mm_and_pd(a, szero));
// 如果 |a| >= 2^52 或者 a 是 NaN,则返回原始值 a;否则返回舍入结果 round
__m128d mask = _mm_cmpge_pd(abs_x, two_power_52);
mask = _mm_or_pd(mask, nan_mask);
return npyv_select_f64(_mm_castpd_si128(mask), a, round);
}
// ceil
// 如果支持 SSE4.1 指令集,则使用 SSE 指令进行向上取整并返回结果
// 如果不支持 SSE4.1 指令集,则手动实现单精度浮点数向上取整操作
NPY_FINLINE npyv_f32 npyv_ceil_f32(npyv_f32 a)
{
const __m128 one = _mm_set1_ps(1.0f);
const __m128 szero = _mm_set1_ps(-0.0f);
const __m128i exp_mask = _mm_set1_epi32(0xff000000);
// 计算非无穷大的掩码,用于排除 NaN 和 Inf 值,避免在比较操作中出现无效的浮点错误
__m128i nfinite_mask = _mm_slli_epi32(_mm_castps_si128(a), 1);
nfinite_mask = _mm_and_si128(nfinite_mask, exp_mask);
nfinite_mask = _mm_cmpeq_epi32(nfinite_mask, exp_mask);
// 处理 NaN 和 Inf 值,将它们的位反转,并与原始值异或,以排除这些值
__m128 x = _mm_xor_ps(a, _mm_castsi128_ps(nfinite_mask));
// 执行舍入操作,并将结果转换为整数
__m128i roundi = _mm_cvtps_epi32(x);
// 将整数结果转换回单精度浮点数
__m128 round = _mm_cvtepi32_ps(roundi);
// 执行向上取整操作
__m128 ceil = _mm_add_ps(round, _mm_and_ps(_mm_cmplt_ps(round, x), one));
// 将符号位从原始值复制到向上取整结果中
ceil = _mm_or_ps(ceil, _mm_and_ps(a, szero));
// 如果溢出,返回原始值 a;否则返回向上取整结果 ceil
__m128i overflow_mask = _mm_cmpeq_epi32(roundi, _mm_castps_si128(szero));
return npyv_select_f32(_mm_or_si128(nfinite_mask, overflow_mask), a, ceil);
}
// 手动实现双精度浮点数向上取整操作
NPY_FINLINE npyv_f64 npyv_ceil_f64(npyv_f64 a)
{
// 创建一个包含值为1.0的双精度浮点数向量
const __m128d one = _mm_set1_pd(1.0);
// 创建一个包含值为-0.0的双精度浮点数向量
const __m128d szero = _mm_set1_pd(-0.0);
// 创建一个包含值为2^52的双精度浮点数向量
const __m128d two_power_52 = _mm_set1_pd(0x10000000000000);
// 创建一个向量,其元素为a中的NaN的掩码
__m128d nan_mask = _mm_cmpunord_pd(a, a);
// 通过异或操作去除NaN,以避免在后续的比较操作中出现无效的浮点数错误
__m128d x = _mm_xor_pd(nan_mask, a);
// 计算x的绝对值向量
__m128d abs_x = npyv_abs_f64(x);
// 计算x的符号向量
__m128d sign_x = _mm_and_pd(x, szero);
// 使用魔术数2^52进行四舍五入
// 假设MXCSR寄存器已设置为四舍五入模式
__m128d round = _mm_sub_pd(_mm_add_pd(two_power_52, abs_x), two_power_52);
// 根据x的符号向量进行符号位的设置
round = _mm_or_pd(round, sign_x);
// 执行向上取整操作
__m128d ceil = _mm_add_pd(round, _mm_and_pd(_mm_cmplt_pd(round, x), one));
// 保持0.0的符号
ceil = _mm_or_pd(ceil, sign_x);
// 如果|a| >= 2^52 或 a == NaN,则返回a,否则返回ceil
__m128d mask = _mm_cmpge_pd(abs_x, two_power_52);
mask = _mm_or_pd(mask, nan_mask);
// 根据掩码选择返回a或ceil的元素
return npyv_select_f64(_mm_castpd_si128(mask), a, ceil);
}
// 如果定义了 NPY_HAVE_SSE41,则定义宏 npyv_floor_f32 为 _mm_floor_ps,即使用 SSE 指令集的单精度浮点数向下取整操作
// 如果定义了 NPY_HAVE_SSE41,则定义宏 npyv_floor_f64 为 _mm_floor_pd,即使用 SSE 指令集的双精度浮点数向下取整操作
// 否则,定义 npyv_floor_f32 函数,对单精度浮点数 a 进行向下取整操作
NPY_FINLINE npyv_f32 npyv_floor_f32(npyv_f32 a)
{
// 设置一个单精度浮点数 -0.0 的向量
const __m128 szero = _mm_set1_ps(-0.0f);
// 设置一个整型向量,其高位字节为 0xff000000
const __m128i exp_mask = _mm_set1_epi32(0xff000000);
// 创建一个掩码,用于标识 a 中有限数字部分的位置
__m128i nfinite_mask = _mm_slli_epi32(_mm_castps_si128(a), 1);
nfinite_mask = _mm_and_si128(nfinite_mask, exp_mask);
nfinite_mask = _mm_cmpeq_epi32(nfinite_mask, exp_mask);
// 消除 NaN 和无穷大,以避免无效的浮点数错误
__m128 x = _mm_xor_ps(a, _mm_castsi128_ps(nfinite_mask));
// 将浮点数 x 向下取整到最接近的整数,得到整型向量
__m128i trunci = _mm_cvttps_epi32(x);
// 将整型向量转换回单精度浮点数向量
__m128 trunc = _mm_cvtepi32_ps(trunci);
// 将负零保留为负零,例如 -0.5 变为 -0.0
trunc = _mm_or_ps(trunc, _mm_and_ps(a, szero));
// 如果溢出则返回原始值 a
__m128i overflow_mask = _mm_cmpeq_epi32(trunci, _mm_castps_si128(szero));
// 如果 a 溢出或不是有限数字,则返回 a;否则返回向下取整后的值
return npyv_select_f32(_mm_or_si128(nfinite_mask, overflow_mask), a, trunc);
}
// 否则,定义 npyv_floor_f64 函数,对双精度浮点数 a 进行向下取整操作
NPY_FINLINE npyv_f64 npyv_floor_f64(npyv_f64 a)
{
// 设置一个双精度浮点数 1.0 的向量
const __m128d one = _mm_set1_pd(1.0);
// 设置一个双精度浮点数 -0.0 的向量
const __m128d szero = _mm_set1_pd(-0.0);
// 设置一个双精度浮点数 2^52 的向量
const __m128d two_power_52 = _mm_set1_pd(0x10000000000000);
// 创建一个掩码,用于标识 a 中 NaN 的位置
__m128d nan_mask = _mm_cmpunord_pd(a, a);
// 消除 NaN,以避免无效的浮点数错误在 cmpge 内
__m128d abs_x = npyv_abs_f64(_mm_xor_pd(nan_mask, a));
// 将 abs_x 向下取整,通过加上 2^52 的魔数来实现
// 假设 MXCSR 寄存器已设置为舍入
__m128d abs_round = _mm_sub_pd(_mm_add_pd(two_power_52, abs_x), two_power_52);
// 计算需要减去的值,以确保正确的向下取整
__m128d subtrahend = _mm_and_pd(_mm_cmpgt_pd(abs_round, abs_x), one);
// 进行向下取整操作
__m128d trunc = _mm_sub_pd(abs_round, subtrahend);
// 赋予结果相同的符号
trunc = _mm_or_pd(trunc, _mm_and_pd(a, szero));
// 如果 |a| >= 2^52 或者 a 是 NaN,则返回 a;否则返回向下取整后的值
__m128d mask = _mm_cmpge_pd(abs_x, two_power_52);
mask = _mm_or_pd(mask, nan_mask);
return npyv_select_f64(_mm_castpd_si128(mask), a, trunc);
}
{
// 定义一个包含单精度浮点数 1.0 的 SSE 寄存器
const __m128 one = _mm_set1_ps(1.0f);
// 定义一个包含单精度浮点数 -0.0 的 SSE 寄存器
const __m128 szero = _mm_set1_ps(-0.0f);
// 定义一个包含 0xff000000 的 SSE 寄存器,用于提取指数部分
const __m128i exp_mask = _mm_set1_epi32(0xff000000);
// 创建一个掩码,用于标识非无穷的浮点数(finite)
__m128i nfinite_mask = _mm_slli_epi32(_mm_castps_si128(a), 1);
nfinite_mask = _mm_and_si128(nfinite_mask, exp_mask);
nfinite_mask = _mm_cmpeq_epi32(nfinite_mask, exp_mask);
// 消除 NaN 和无穷大,以避免无效的浮点数错误
__m128 x = _mm_xor_ps(a, _mm_castsi128_ps(nfinite_mask));
// 将浮点数转换为整数并进行舍入
__m128i roundi = _mm_cvtps_epi32(x);
// 将整数舍入结果转换回浮点数
__m128 round = _mm_cvtepi32_ps(roundi);
// 计算向下取整结果
__m128 floor = _mm_sub_ps(round, _mm_and_ps(_mm_cmpgt_ps(round, x), one));
// 考虑到带符号的零
floor = _mm_or_ps(floor, _mm_and_ps(a, szero));
// 如果发生溢出,则返回原始值 a
__m128i overflow_mask = _mm_cmpeq_epi32(roundi, _mm_castps_si128(szero));
// 如果数值溢出或者是非有限浮点数,则返回 a,否则返回 floor 的结果
return npyv_select_f32(_mm_or_si128(nfinite_mask, overflow_mask), a, floor);
}
NPY_FINLINE npyv_f64 npyv_floor_f64(npyv_f64 a)
{
// 定义一个包含双精度浮点数 1.0 的 SSE 寄存器
const __m128d one = _mm_set1_pd(1.0);
// 定义一个包含双精度浮点数 -0.0 的 SSE 寄存器
const __m128d szero = _mm_set1_pd(-0.0);
// 定义一个包含 2^52 的双精度浮点数的 SSE 寄存器
const __m128d two_power_52 = _mm_set1_pd(0x10000000000000);
// 创建一个 NaN 掩码,用于标识 NaN 值
__m128d nan_mask = _mm_cmpunord_pd(a, a);
// 消除 NaN 以避免在 cmpge 内部出现无效的浮点数错误
__m128d x = _mm_xor_pd(nan_mask, a);
// 计算绝对值
__m128d abs_x = npyv_abs_f64(x);
// 提取符号位
__m128d sign_x = _mm_and_pd(x, szero);
// 通过加上魔法数 2^52 进行舍入
// 假设 MXCSR 寄存器已设置为四舍五入
__m128d round = _mm_sub_pd(_mm_add_pd(two_power_52, abs_x), two_power_52);
// 复制符号位
round = _mm_or_pd(round, sign_x);
// 计算向下取整结果
__m128d floor = _mm_sub_pd(round, _mm_and_pd(_mm_cmpgt_pd(round, x), one));
// 如果 |a| >= 2^52 或者 a 是 NaN,则返回 a;否则返回 floor 的结果
__m128d mask = _mm_cmpge_pd(abs_x, two_power_52);
mask = _mm_or_pd(mask, nan_mask);
return npyv_select_f64(_mm_castpd_si128(mask), a, floor);
}
// 结束条件:如果定义了 NPY_HAVE_SSE41 宏,则结束当前代码块
// 结束条件:结束 _NPY_SIMD_SSE_MATH_H 头文件的条件编译块
.\numpy\numpy\_core\src\common\simd\sse\memory.h
/***************************
* load/store
***************************/
// stream load
// 定义 SSE 内存操作的模板宏,参数为数据类型 CTYPE 和后缀 SFX
// 加载未对齐的数据并转换为 SSE 寄存器格式
NPY_FINLINE npyv_
{ return _mm_loadu_si128((const __m128i*)ptr); } \
// 加载对齐的数据并转换为 SSE 寄存器格式
NPY_FINLINE npyv_
{ return _mm_load_si128((const __m128i*)ptr); } \
// 使用 stream 方式加载数据,根据是否支持 SSE4.1 决定具体实现
NPY_FINLINE npyv_
{ return npyv__loads(ptr); } \
// 加载 64 位数据的低位部分并转换为 SSE 寄存器格式
NPY_FINLINE npyv_
{ return _mm_loadl_epi64((const __m128i*)ptr); } \
// 存储 SSE 寄存器数据到未对齐的内存位置
NPY_FINLINE void npyv_store_
{ _mm_storeu_si128((__m128i*)ptr, vec); } \
// 存储 SSE 寄存器数据到对齐的内存位置
NPY_FINLINE void npyv_storea_
{ _mm_store_si128((__m128i*)ptr, vec); } \
// 使用 stream 方式存储 SSE 寄存器数据到内存位置
NPY_FINLINE void npyv_stores_
{ _mm_stream_si128((__m128i*)ptr, vec); } \
// 存储 SSE 寄存器数据的低位部分到内存位置
NPY_FINLINE void npyv_storel_
{ _mm_storel_epi64((__m128i *)ptr, vec); } \
// 存储 SSE 寄存器数据的高位部分到内存位置,使用 _mm_unpackhi_epi64 实现
NPY_FINLINE void npyv_storeh_
{ _mm_storel_epi64((__m128i *)ptr, _mm_unpackhi_epi64(vec, vec)); }
// 各种数据类型的 SSE 内存操作宏的具体实现
NPYV_IMPL_SSE_MEM_INT(npy_uint8, u8)
NPYV_IMPL_SSE_MEM_INT(npy_int8, s8)
NPYV_IMPL_SSE_MEM_INT(npy_uint16, u16)
NPYV_IMPL_SSE_MEM_INT(npy_int16, s16)
NPYV_IMPL_SSE_MEM_INT(npy_uint32, u32)
NPYV_IMPL_SSE_MEM_INT(npy_int32, s32)
NPYV_IMPL_SSE_MEM_INT(npy_uint64, u64)
NPYV_IMPL_SSE_MEM_INT(npy_int64, s64)
// unaligned load
// aligned load
// load lower part
// 加载浮点数低位部分并转换为 SSE 寄存器格式,使用 _mm_castsi128_ps 转换为 __m128 类型
// stream load
// 使用 stream 方式加载浮点数并转换为 SSE 寄存器格式,使用 _mm_castsi128_ps 转换为 __m128 类型
// unaligned store
// aligned store
// stream store
// 存储向量低部分为 32 位整数
// 存储向量低部分为 64 位双精度浮点数
// 存储向量高部分为 32 位单精度浮点数
// 存储向量高部分为 64 位双精度浮点数
/***************************
* 非连续加载
***************************/
//// 32 位整数加载
NPY_FINLINE npyv_s32 npyv_loadn_s32(const npy_int32 *ptr, npy_intp stride)
{
// 将首地址的整数加载到 xmm 寄存器中
__m128i a = _mm_cvtsi32_si128(*ptr);
// 使用 SSE4.1 插入指令加载后续整数到 xmm 寄存器
a = _mm_insert_epi32(a, ptr[stride], 1);
a = _mm_insert_epi32(a, ptr[stride*2], 2);
a = _mm_insert_epi32(a, ptr[stride*3], 3);
// 使用非 SSE4.1 插入方法加载后续整数到 xmm 寄存器
__m128i a1 = _mm_cvtsi32_si128(ptr[stride]);
__m128i a2 = _mm_cvtsi32_si128(ptr[stride*2]);
__m128i a3 = _mm_cvtsi32_si128(ptr[stride*3]);
a = _mm_unpacklo_epi32(a, a1);
a = _mm_unpacklo_epi64(a, _mm_unpacklo_epi32(a2, a3));
return a;
}
// 无符号 32 位整数加载,实际调用有符号加载函数
NPY_FINLINE npyv_u32 npyv_loadn_u32(const npy_uint32 *ptr, npy_intp stride)
{ return npyv_loadn_s32((const npy_int32*)ptr, stride); }
// 单精度浮点数加载,转换整数加载函数返回结果为浮点数
NPY_FINLINE npyv_f32 npyv_loadn_f32(const float *ptr, npy_intp stride)
{ return _mm_castsi128_ps(npyv_loadn_s32((const npy_int32*)ptr, stride)); }
//// 64 位双精度浮点数加载
NPY_FINLINE npyv_f64 npyv_loadn_f64(const double *ptr, npy_intp stride)
{ return _mm_loadh_pd(npyv_loadl_f64(ptr), ptr + stride); }
// 无符号 64 位整数加载,实际调用双精度加载函数
NPY_FINLINE npyv_u64 npyv_loadn_u64(const npy_uint64 *ptr, npy_intp stride)
{ return _mm_castpd_si128(npyv_loadn_f64((const double*)ptr, stride)); }
// 有符号 64 位整数加载,实际调用双精度加载函数
NPY_FINLINE npyv_s64 npyv_loadn_s64(const npy_int64 *ptr, npy_intp stride)
{ return _mm_castpd_si128(npyv_loadn_f64((const double*)ptr, stride)); }
//// 64 位加载,步长为 32 位整数
NPY_FINLINE npyv_f32 npyv_loadn2_f32(const float *ptr, npy_intp stride)
{
// 加载低部分双精度浮点数到 xmm 寄存器
__m128d r = _mm_loadh_pd(
npyv_loadl_f64((const double*)ptr), (const double*)(ptr + stride)
);
return _mm_castpd_ps(r); // 转换为单精度浮点数返回
}
// 无符号 32 位整数加载,实际调用单精度加载函数
NPY_FINLINE npyv_u32 npyv_loadn2_u32(const npy_uint32 *ptr, npy_intp stride)
{ return _mm_castps_si128(npyv_loadn2_f32((const float*)ptr, stride)); }
// 有符号 32 位整数加载,实际调用单精度加载函数
NPY_FINLINE npyv_s32 npyv_loadn2_s32(const npy_int32 *ptr, npy_intp stride)
{ return _mm_castps_si128(npyv_loadn2_f32((const float*)ptr, stride)); }
//// 128 位加载,步长为 64 位双精度浮点数
NPY_FINLINE npyv_f64 npyv_loadn2_f64(const double *ptr, npy_intp stride)
{ (void)stride; return npyv_load_f64(ptr); } // 直接加载双精度浮点数
// 无符号 64 位整数加载,实际调用双精度加载函数
NPY_FINLINE npyv_u64 npyv_loadn2_u64(const npy_uint64 *ptr, npy_intp stride)
{ (void)stride; return npyv_load_u64(ptr); }
// 有符号 64 位整数加载,实际调用双精度加载函数
NPY_FINLINE npyv_s64 npyv_loadn2_s64(const npy_int64 *ptr, npy_intp stride)
{ (void)stride; return npyv_load_s64(ptr); }
/***************************
* 非连续存储
***************************/
//// 32 位整数存储
NPY_FINLINE void npyv_storen_s32(npy_int32 *ptr, npy_intp stride, npyv_s32 a)
{
// 存储 xmm 寄存器中整数的低部分到目标地址
ptr[stride * 0] = _mm_cvtsi128_si32(a);
ptr[stride * 1] = _mm_extract_epi32(a, 1);
ptr[stride * 2] = _mm_extract_epi32(a, 2);
ptr[stride * 3] = _mm_extract_epi32(a, 3);
//// 64
NPY_FINLINE void npyv_storen_f64(double *ptr, npy_intp stride, npyv_f64 a)
{
// 使用 SSE2 指令将双精度浮点数向量 a 存储到 ptr 和 ptr + stride 处
_mm_storel_pd(ptr, a); // 将向量 a 的低64位存储到 ptr
_mm_storeh_pd(ptr + stride, a); // 将向量 a 的高64位存储到 ptr + stride
}
//// 64-bit store over 32-bit stride
NPY_FINLINE void npyv_storen2_u32(npy_uint32 *ptr, npy_intp stride, npyv_u32 a)
{
// 使用 SSE2 指令将32位无符号整数向量 a 存储到 ptr 和 ptr + stride 处
_mm_storel_pd((double*)ptr, _mm_castsi128_pd(a)); // 将向量 a 转换为双精度浮点数后存储到 ptr
_mm_storeh_pd((double*)(ptr + stride), _mm_castsi128_pd(a)); // 将向量 a 转换为双精度浮点数后存储到 ptr + stride
}
/*********************************
* Partial Load
*********************************/
//// 32
NPY_FINLINE npyv_s32 npyv_load_till_s32(const npy_int32 *ptr, npy_uintp nlane, npy_int32 fill)
{
assert(nlane > 0); // 断言 nlane 大于 0,确保加载的长度有效
const short *wptr = (const short*)ptr; // 如果没有 SSE4.1 支持,将 ptr 视为 short 指针
const __m128i vfill = npyv_setall_s32(fill); // 使用 SSE2 指令生成填充值 fill 的向量 vfill
__m128i a;
switch(nlane) {
case 2:
a = _mm_castpd_si128(
_mm_loadl_pd(_mm_castsi128_pd(vfill), (double*)ptr)
);
break;
case 1:
a = _mm_insert_epi32(vfill, ptr[0], 0); // 使用 SSE4.1 指令在 vfill 中插入第一个整数值
break;
case 3:
a = _mm_loadl_epi64((const __m128i*)ptr); // 使用 SSE2 指令加载 ptr 所指向的64位数据到向量 a
a = _mm_insert_epi32(a, ptr[2], 2); // 在向量 a 中的第2个位置插入 ptr[2] 的值
a = _mm_insert_epi32(a, fill, 3); // 在向量 a 中的第3个位置插入 fill 的值
break;
case 1:
a = _mm_insert_epi16(vfill, wptr[0], 0); // 使用 SSE2 指令在 vfill 中插入第一个短整数值
a = _mm_insert_epi16(a, wptr[1], 1);
break;
case 3:
a = _mm_loadl_epi64((const __m128i*)ptr);
a = _mm_unpacklo_epi64(a, vfill);
a = _mm_insert_epi16(a, wptr[4], 4);
a = _mm_insert_epi16(a, wptr[5], 5);
break;
default:
return npyv_load_s32(ptr);
// We use a variable marked 'volatile' to convince the compiler that
// the entire vector is needed.
volatile __m128i workaround = a;
// avoid optimizing it out
a = _mm_or_si128(workaround, a);
return a;
// 在非常量数组指针ptr指向的地址上,根据整型元素读取数据,直到填充nlane数量的32位整型数据
NPY_FINLINE npyv_s32
npyv_loadn_till_s32(const npy_int32 *ptr, npy_intp stride, npy_uintp nlane, npy_int32 fill)
{
// 断言保证加载的元素数量大于0
assert(nlane > 0);
// 使用指定的32位整数填充创建一个__m128i类型的向量vfill
__m128i vfill = npyv_setall_s32(fill);
// 如果不支持SSE4.1指令集
// 将ptr转换为short型指针wptr
const short *wptr = (const short*)ptr;
// 根据nlane的数量进行switch选择
switch(nlane) {
这段代码的主要作用是根据传入的指针和参数,加载一定数量的32位整数数据到SSE向量中,支持非连续的部分加载。
// 如果定义了 NPY_HAVE_SSE41 宏,则执行以下代码块
case 3:
// 将 ptr[stride*2] 插入到 vfill 的第 2 个位置
vfill = _mm_insert_epi32(vfill, ptr[stride*2], 2);
case 2:
// 将 ptr[stride] 插入到 vfill 的第 1 个位置
vfill = _mm_insert_epi32(vfill, ptr[stride], 1);
case 1:
// 将 ptr[0] 插入到 vfill 的第 0 个位置
vfill = _mm_insert_epi32(vfill, ptr[0], 0);
// 跳出 switch 语句
break;
// 如果未定义 NPY_HAVE_SSE41 宏,则执行以下代码块
case 3:
// 将 ptr[stride*2] 和 vfill 进行低位展开,组成新的 vfill
vfill = _mm_unpacklo_epi32(_mm_cvtsi32_si128(ptr[stride*2]), vfill);
case 2:
// 将 ptr[0] 和 ptr[stride] 以及当前的 vfill 进行展开操作,组成新的 vfill
vfill = _mm_unpacklo_epi64(_mm_unpacklo_epi32(
_mm_cvtsi32_si128(*ptr), _mm_cvtsi32_si128(ptr[stride])
), vfill);
// 跳出 switch 语句
break;
case 1:
// 将 wptr[0] 插入到 vfill 的第 0 个位置
vfill = _mm_insert_epi16(vfill, wptr[0], 0);
// 将 wptr[1] 插入到 vfill 的第 1 个位置
vfill = _mm_insert_epi16(vfill, wptr[1], 1);
// 跳出 switch 语句
break;
// 如果不匹配上述任何 case,执行默认情况
default:
// 调用 npyv_loadn_s32 函数加载 ptr 和 stride 所指示的数据
return npyv_loadn_s32(ptr, stride);
} // switch 结束
// 如果定义了 NPY_SIMD_GUARD_PARTIAL_LOAD 宏,则执行以下代码块
volatile __m128i workaround = vfill;
// 创建一个 volatile 的 __m128i 变量 workaround,用 vfill 初始化它
vfill = _mm_or_si128(workaround, vfill);
// 使用逻辑或操作符将 workaround 和 vfill 合并,并将结果赋给 vfill
// 返回 vfill 变量的值
return vfill;
}
// 填充剩余的通道为零
NPY_FINLINE npyv_s32
npyv_loadn_tillz_s32(const npy_int32 *ptr, npy_intp stride, npy_uintp nlane)
{
assert(nlane > 0);
// 断言 nlane 大于 0
switch(nlane) {
case 1:
// 当 nlane 为 1 时执行以下代码块
return _mm_cvtsi32_si128(ptr[0]);
// 使用 ptr[0] 创建一个 __m128i 类型的变量并返回
case 2:;
// 当 nlane 为 2 时执行以下代码块
{
// 创建一个局部的 npyv_s32 类型变量 a,并用 ptr[0] 初始化它
npyv_s32 a = _mm_cvtsi32_si128(ptr[0]);
// 如果定义了 NPY_HAVE_SSE41 宏,则执行以下代码块
return _mm_insert_epi32(a, ptr[stride], 1);
// 使用 _mm_insert_epi32 在 a 的第二个位置插入 ptr[stride],并返回结果
// 如果未定义 NPY_HAVE_SSE41 宏,则执行以下代码块
return _mm_unpacklo_epi32(a, _mm_cvtsi32_si128(ptr[stride]));
// 使用 _mm_unpacklo_epi32 将 a 和 ptr[stride] 的数据组合,并返回结果
}
case 3:
// 当 nlane 为 3 时执行以下代码块
{
// 创建一个局部的 npyv_s32 类型变量 a,并用 ptr[0] 初始化它
npyv_s32 a = _mm_cvtsi32_si128(ptr[0]);
// 如果定义了 NPY_HAVE_SSE41 宏,则执行以下代码块
a = _mm_insert_epi32(a, ptr[stride], 1);
// 使用 _mm_insert_epi32 在 a 的第二个位置插入 ptr[stride]
a = _mm_insert_epi32(a, ptr[stride*2], 2);
// 使用 _mm_insert_epi32 在 a 的第三个位置插入 ptr[stride*2]
return a;
// 返回已经填充好数据的 a
// 如果未定义 NPY_HAVE_SSE41 宏,则执行以下代码块
a = _mm_unpacklo_epi32(a, _mm_cvtsi32_si128(ptr[stride]));
// 使用 _mm_unpacklo_epi32 将 a 和 ptr[stride] 的数据组合
a = _mm_unpacklo_epi64(a, _mm_cvtsi32_si128(ptr[stride*2]));
// 使用 _mm_unpacklo_epi64 将 a 和 ptr[stride*2] 的数据组合
return a;
// 返回已经填充好数据的 a
}
default:
// 默认情况下
return npyv_loadn_s32(ptr, stride);
// 调用 npyv_loadn_s32 函数并返回结果
}
}
//// 64
NPY_FINLINE npyv_s64
npyv_loadn_till_s64(const npy_int64 *ptr, npy_intp stride, npy_uintp nlane, npy_int64 fill)
{
assert(nlane > 0);
// 断言 nlane 大于 0
if (nlane == 1) {
// 如果 nlane 等于 1,则执行以下代码块
return npyv_load_till_s64(ptr, 1, fill);
// 调用 npyv_load_till_s64 函数并返回结果
}
return npyv_loadn_s64(ptr, stride);
// 调用 npyv_loadn_s64 函数并返回结果
}
// 填充剩余的通道为零
NPY_FINLINE npyv_s64 npyv_loadn_tillz_s64(const npy_int64 *ptr, npy_intp stride, npy_uintp nlane)
{
assert(nlane > 0);
// 断言 nlane 大于 0
if (nlane == 1) {
// 如果 nlane 等于 1,则执行以下代码块
return _mm_loadl_epi64((const __m128i*)ptr);
// 使用 _mm_loadl_epi64 加载 ptr 所指向的数据,并返回结果
}
return npyv_loadn_s64(ptr, stride);
// 调用 npyv_loadn_s64 函数并返回结果
}
//// 64-bit load over 32-bit stride
NPY_FINLINE npyv_s32 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);
// 断言 nlane 大于 0
if (nlane == 1) {
// 如果 nlane 等于 1,则执行以下代码块
const __m128i vfill = npyv_set_s32(0, 0, fill_lo, fill_hi);
// 使用 npyv_set_s32 函数创建一个 __m128i 类型的 vfill 变量
__m128i a = _mm_castpd_si128(
_mm_loadl_pd(_mm_castsi128_pd(vfill), (double*)ptr)
);
// 使用 _mm_loadl_pd 和 _mm_castpd_si128 加载数据到 a 变量
// 如果定义了 NPY_SIMD_GUARD_PARTIAL_LOAD 宏,则执行以下代码块
volatile __m128i workaround = a;
// 创建一个 volatile 的 __m128i 变量 workaround,用 a 初始化它
a = _mm_or_si128(workaround, a);
// 使用逻辑或操作符将 workaround 和 a 合并,并将结果赋给 a
// 返回 a 变量的值
return a;
}
return npyv_loadn2_s32(ptr, stride);
// 调用 npyv_loadn2_s32 函数并返回结果
}
NPY_FINLINE npyv_s32 npyv_loadn2_tillz_s32(const npy_int32 *ptr, npy_intp stride, npy_uintp nlane)
{
assert(nlane > 0);
// 断言 nlane 大于 0
if (nlane == 1) {
// 如果 nlane 等于 1,则执行以下代码块
return _mm_loadl_epi64((const __m128i*)ptr);
// 使用 _mm_loadl_epi64 加载 ptr 所指向的数据,并返回结果
}
return npyv_loadn2_s32(ptr, stride);
// 调用 npyv_loadn2_s32 函数并返回结果
}
//// 128-bit load over 64-bit stride
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); (void)stride; (void)nlane; (void)fill_lo; (void)fill_hi; return npyv_load_s64(ptr); }
// 断言 nlane 大于 0,并调用 npyv_load_s64 函数并返回结果
//// 32
NPY_FINLINE void npyv_store_till_s32(npy_int32 *ptr, npy_uintp nlane, npyv_s32 a)
{
// 确保 nlane 大于 0,即至少有一个元素需要存储
assert(nlane > 0);
// 根据 nlane 的值选择不同的存储方式
switch(nlane) {
case 1:
// 当 nlane 为 1 时,将 a 的低位 32 位存储到 ptr 指向的位置
*ptr = _mm_cvtsi128_si32(a);
break;
case 2:
// 当 nlane 为 2 时,将 a 的低位 64 位存储到 ptr 指向的位置
_mm_storel_epi64((__m128i *)ptr, a);
break;
case 3:
// 当 nlane 为 3 时,将 a 的低位 64 位存储到 ptr 指向的位置
_mm_storel_epi64((__m128i *)ptr, a);
// 如果支持 SSE4.1,则额外将 a 的第三个 32 位整数存储到 ptr 指向的位置
ptr[2] = _mm_extract_epi32(a, 2);
// 如果不支持 SSE4.1,则通过重新排列获取第三个 32 位整数并存储到 ptr 指向的位置
ptr[2] = _mm_cvtsi128_si32(_mm_shuffle_epi32(a, _MM_SHUFFLE(0, 0, 0, 2)));
break;
default:
// 对于其他情况,调用通用的存储函数 npyv_store_s32 将 a 中的所有数据存储到 ptr 指向的位置
npyv_store_s32(ptr, a);
}
}
//// 64
NPY_FINLINE void npyv_store_till_s64(npy_int64 *ptr, npy_uintp nlane, npyv_s64 a)
{
// 确保 nlane 大于 0,即至少有一个元素需要存储
assert(nlane > 0);
// 当 nlane 为 1 时,将 a 的低位 64 位存储到 ptr 指向的位置
if (nlane == 1) {
_mm_storel_epi64((__m128i *)ptr, a);
return;
}
// 对于其他情况,调用通用的存储函数 npyv_store_s64 将 a 中的所有数据存储到 ptr 指向的位置
npyv_store_s64(ptr, a);
}
//// 64-bit nlane
NPY_FINLINE void npyv_store2_till_s32(npy_int32 *ptr, npy_uintp nlane, npyv_s32 a)
{
// 确保 nlane 大于 0,即至少有一个元素需要存储
assert(nlane > 0);
// 调用 npyv_store_till_s64 将 a 中的数据存储到 ptr 指向的位置
npyv_store_till_s64((npy_int64*)ptr, nlane, a);
}
//// 128-bit nlane
NPY_FINLINE void npyv_store2_till_s64(npy_int64 *ptr, npy_uintp nlane, npyv_s64 a)
{
// 确保 nlane 大于 0,即至少有一个元素需要存储
assert(nlane > 0);
// 调用通用的存储函数 npyv_store_s64 将 a 中的所有数据存储到 ptr 指向的位置
npyv_store_s64(ptr, 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)
{
// 确保 nlane 大于 0,即至少有一个元素需要存储
assert(nlane > 0);
// 将 a 的低位 32 位整数存储到 ptr 指向的位置(根据 stride 和索引计算存储位置)
ptr[stride*0] = _mm_cvtsi128_si32(a);
// 根据 nlane 的值选择不同的存储方式
switch(nlane) {
case 1:
return;
case 2:
// 如果支持 SSE4.1,则将 a 的第二个 32 位整数存储到 ptr 指向的位置
ptr[stride*1] = _mm_extract_epi32(a, 1);
return;
case 3:
// 如果支持 SSE4.1,则将 a 的第二和第三个 32 位整数存储到 ptr 指向的位置
ptr[stride*1] = _mm_extract_epi32(a, 1);
ptr[stride*2] = _mm_extract_epi32(a, 2);
return;
default:
// 如果支持 SSE4.1,则将 a 的第二到第四个 32 位整数存储到 ptr 指向的位置
ptr[stride*1] = _mm_extract_epi32(a, 1);
ptr[stride*2] = _mm_extract_epi32(a, 2);
ptr[stride*3] = _mm_extract_epi32(a, 3);
case 2:
// 如果不支持 SSE4.1,则通过重新排列获取第二个 32 位整数并存储到 ptr 指向的位置
ptr[stride*1] = _mm_cvtsi128_si32(_mm_shuffle_epi32(a, _MM_SHUFFLE(0, 0, 0, 1)));
return;
case 3:
// 如果不支持 SSE4.1,则通过重新排列获取第二和第三个 32 位整数并存储到 ptr 指向的位置
ptr[stride*1] = _mm_cvtsi128_si32(_mm_shuffle_epi32(a, _MM_SHUFFLE(0, 0, 0, 1)));
ptr[stride*2] = _mm_cvtsi128_si32(_mm_shuffle_epi32(a, _MM_SHUFFLE(0, 0, 0, 2)));
return;
default:
// 如果不支持 SSE4.1,则通过重新排列获取第二到第四个 32 位整数并存储到 ptr 指向的位置
ptr[stride*1] = _mm_cvtsi128_si32(_mm_shuffle_epi32(a, _MM_SHUFFLE(0, 0, 0, 1)));
ptr[stride*2] = _mm_cvtsi128_si32(_mm_shuffle_epi32(a, _MM_SHUFFLE(0, 0, 0, 2)));
ptr[stride*3] = _mm_cvtsi128_si32(_mm_shuffle_epi32(a, _MM_SHUFFLE(0, 0, 0, 3)));
}
}
//// 64
NPY_FINLINE void npyv_storen_till_s64(npy_int64 *ptr, npy_intp stride, npy_uintp nlane, npyv_s64 a)
{
// 确保 nlane 大于 0,即至少有一个元素需要存储
assert(nlane > 0);
// 当 nlane 为 1 时,将 a 的低位 64 位存储到 ptr 指向的位置
if (nlane == 1) {
_mm_storel_epi64((__m128i *)ptr, a);
return;
}
// 调用通用的存储函数 npyv_storen_s64 将 a 中的所有数据按照指定的 stride 存储到 ptr 指向的位置
npyv_storen_s64(ptr, stride, a);
}
//// 64-bit store over 32-bit stride
// 定义了一个函数 npyv_storen2_till_s32,用于将 64 位整数存储到 32 位步长位置
NPY_FINLINE void npyv_storen2_till_s32(npy_int32 *ptr, npy_intp stride, npy_uintp nlane, npyv_s32 a)
{
// 断言确保 nlane 大于 0
assert(nlane > 0);
// 将向量 a 的低位存储到 ptr 指向的位置
npyv_storel_s32(ptr, a);
// 如果 nlane 大于 1,则将向量 a 的高位存储到 ptr + stride 指向的位置
if (nlane > 1) {
npyv_storeh_s32(ptr + stride, a);
}
}
//// 128-bit store over 64-bit stride
// 定义了一个函数 npyv_storen2_till_s64,用于将 128 位整数存储到 64 位步长位置
NPY_FINLINE void npyv_storen2_till_s64(npy_int64 *ptr, npy_intp stride, npy_uintp nlane, npyv_s64 a)
{
// 断言确保 nlane 大于 0
assert(nlane > 0);
// 忽略 stride 和 nlane 的值,将向量 a 存储到 ptr 指向的位置
(void)stride; (void)nlane; npyv_store_s64(ptr, a);
}
/*****************************************************************
* Implement partial load/store for u32/f32/u64/f64... via casting
*****************************************************************/
// 定义了一系列宏,实现了通过类型转换来进行部分加载/存储操作,支持 u32/f32/u64/f64 等类型
// 实现了加载函数 npyv_load_till_
NPY_FINLINE npyv_
(const npyv_lanetype_
{ \
// 定义一个联合体 pun,用于将 fill 转换成目标类型 T_SFX
union { \
npyv_lanetype_
npyv_lanetype_
} pun; \
pun.from_
// 返回将转换后的填充值 pun.to_
return npyv_reinterpret_
(const npyv_lanetype_
)); \
} \
// 实现了加载函数 npyv_loadn_till_
NPY_FINLINE npyv_
(const npyv_lanetype_
npyv_lanetype_
{ \
union { \
npyv_lanetype_
npyv_lanetype_
} pun; \
pun.from_
// 使用联合体 pun 将 fill 转换为 from_
return npyv_reinterpret_
(const npyv_lanetype_
)); \
} \
// 以非常规方式加载 F_SFX 类型的向量,直接从指针 ptr 中读取 nlane 个元素
NPY_FINLINE npyv_
(const npyv_lanetype_
{ \
// 将加载到的数据重新解释为 T_SFX 类型的向量
return npyv_reinterpret_
(const npyv_lanetype_
)); \
} \
// 以非常规方式加载 F_SFX 类型的向量,并且在末尾填充零值
NPY_FINLINE npyv_
(const npyv_lanetype_
{ \
// 将加载到的数据重新解释为 T_SFX 类型的向量
return npyv_reinterpret_
(const npyv_lanetype_
)); \
} \
// 以非常规方式存储 F_SFX 类型的向量数据
NPY_FINLINE void npyv_store_till_
(npyv_lanetype_
{ \
// 将 F_SFX 类型的向量 a 重新解释为 T_SFX 类型后存储到 ptr 指向的内存中
npyv_store_till_
(npyv_lanetype_
npyv_reinterpret_
); \
npyv_store_till_
} \
// 定义 NPY_FINLINE 宏展开后的函数 npyv_storen_till_
NPY_FINLINE void npyv_storen_till_
// 函数签名:将 npyv_lanetype_
(npyv_lanetype_
{ \
// 调用宏 npyv_storen_till_
npyv_storen_till_
(npyv_lanetype_
npyv_reinterpret_
); \
}
// 定义宏 NPYV_IMPL_SSE_REST_PARTIAL_TYPES,用于生成特定数据类型和操作类型的函数实现
NPYV_IMPL_SSE_REST_PARTIAL_TYPES(u32, s32)
// 生成 npyv_u32 类型的 SSE 函数实现,处理 s32 类型操作
NPYV_IMPL_SSE_REST_PARTIAL_TYPES(f32, s32)
// 生成 npyv_f32 类型的 SSE 函数实现,处理 s32 类型操作
NPYV_IMPL_SSE_REST_PARTIAL_TYPES(u64, s64)
// 生成 npyv_u64 类型的 SSE 函数实现,处理 s64 类型操作
NPYV_IMPL_SSE_REST_PARTIAL_TYPES(f64, s64)
// 生成 npyv_f64 类型的 SSE 函数实现,处理 s64 类型操作
// 定义宏 NPYV_IMPL_SSE_REST_PARTIAL_TYPES_PAIR,用于生成两种数据类型组合的函数实现
// 声明 npyv_load2_till_
NPY_FINLINE npyv_
(const npyv_lanetype_
npyv_lanetype_
{ \
// 定义联合体 pun 用于类型转换
union pun { \
npyv_lanetype_
npyv_lanetype_
}; \
// 定义联合体变量 pun_lo 和 pun_hi,分别填充低位和高位的值
union pun pun_lo; \
union pun pun_hi; \
pun_lo.from_
pun_hi.from_
// 返回根据填充值转换后的结果,使用 npyv_load2_till_
return npyv_reinterpret_
(const npyv_lanetype_
)); \
} \
// 声明 npyv_loadn2_till_
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_
)); \
} \
这段代码定义了一个函数,用于加载并重新解释 SIMD 向量数据类型。根据不同的输入类型转换加载,以及指定的填充值。
NPY_FINLINE npyv_
(const npyv_lanetype_
{ \
return npyv_reinterpret_
(const npyv_lanetype_
)); \
} \
这段代码定义了一个函数,用于加载指定数量的 SIMD 向量数据类型并清零剩余部分。
NPY_FINLINE npyv_
(const npyv_lanetype_
{ \
return npyv_reinterpret_
(const npyv_lanetype_
)); \
} \
这段代码定义了一个函数,用于加载带有步长的 SIMD 向量数据类型,并清零剩余部分。
NPY_FINLINE void npyv_store2_till_
(npyv_lanetype_
这段代码定义了一个函数,用于存储指定数量的 SIMD 向量数据类型。
{
npyv_store2_till_
(npyv_lanetype_
npyv_reinterpret_
);
NPY_FINLINE void npyv_storen2_till_
(npyv_lanetype_
{
npyv_storen2_till_
(npyv_lanetype_
npyv_reinterpret_
);
}
}
// 定义一个宏,用于生成特定类型的 SIMD 操作函数,实现加载和存储的互相关联
// 实现类型为 TYPE1 和 TYPE2 的 SSE 操作的特定函数
/************************************************************
* de-interlave load / interleave contiguous store
************************************************************/
// 两个通道的内存交织加载和连续存储
// 定义一个宏,用于实现给定类型的内存交织加载和存储操作
// 内存交织加载操作,将连续存储的数据分别加载到两个向量中
NPY_FINLINE npyv_
// 内存解交织操作,将两个向量的数据合并为一个向量
NPY_FINLINE npyv_
// 加载两个连续存储的数据块,分别存储到两个向量中
NPY_FINLINE npyv_
const npyv_lanetype_
) { \
return npyv_unzip_
npyv_load_
); \
} \
// 将两个向量的数据分别存储到连续存储块中
NPY_FINLINE void npyv_store_
npyv_lanetype_
) { \
npyv_
npyv_store_
npyv_store_
}
// 以下是根据上述宏定义生成的具体类型的内存交织加载和存储操作函数
NPYV_IMPL_SSE_MEM_INTERLEAVE(u8, u8)
NPYV_IMPL_SSE_MEM_INTERLEAVE(s8, u8)
NPYV_IMPL_SSE_MEM_INTERLEAVE(u16, u16)
NPYV_IMPL_SSE_MEM_INTERLEAVE(s16, u16)
NPYV_IMPL_SSE_MEM_INTERLEAVE(u32, u32)
NPYV_IMPL_SSE_MEM_INTERLEAVE(s32, u32)
NPYV_IMPL_SSE_MEM_INTERLEAVE(u64, u64)
NPYV_IMPL_SSE_MEM_INTERLEAVE(s64, u64)
NPYV_IMPL_SSE_MEM_INTERLEAVE(f32, f32)
NPYV_IMPL_SSE_MEM_INTERLEAVE(f64, f64)
/*********************************
* Lookup table
*********************************/
// 使用向量作为索引查找包含 32 个 float32 元素的表格
// 返回根据索引 idx 查找的 float32 类型的表格中的值组成的向量
NPY_FINLINE npyv_f32 npyv_lut32_f32(const float *table, npyv_u32 idx)
{
// 提取 idx 向量的第一个元素作为整数索引 i0
const int i0 = _mm_cvtsi128_si32(idx);
// 使用 SSE4.1 的函数从 idx 向量中提取其他整数索引 i1, i2, i3
const int i1 = _mm_extract_epi32(idx, 1);
const int i2 = _mm_extract_epi32(idx, 2);
const int i3 = _mm_extract_epi32(idx, 3);
// 使用 SSE2 的函数从 idx 向量中提取其他整数索引 i1, i2, i3
const int i1 = _mm_extract_epi16(idx, 2);
const int i2 = _mm_extract_epi16(idx, 4);
const int i3 = _mm_extract_epi16(idx, 6);
// 返回根据索引从表格中取出的四个 float32 值组成的向量
return npyv_set_f32(table[i0], table[i1], table[i2], table[i3]);
}
// 返回根据索引 idx 查找的 uint32 类型的表格中的值组成的向量
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)); }
// 返回根据索引 idx 查找的 int32 类型的表格中的值组成的向量
NPY_FINLINE npyv_s32 npyv_lut32_s32(const npy_int32 *table, npyv_u32 idx)
// 使用给定的索引向表中查找对应的双精度浮点数,返回一个双精度向量
NPY_FINLINE npyv_f64 npyv_lut16_f64(const double *table, npyv_u64 idx)
{
// 从 idx 中提取第一个索引 i0,并转换为整数
const int i0 = _mm_cvtsi128_si32(idx);
// 如果支持 SSE4.1,则从 idx 中提取第二个索引 i1(32位),否则从 idx 中提取第二个索引 i1(16位)
const int i1 = _mm_extract_epi32(idx, 2);
const int i1 = _mm_extract_epi16(idx, 4);
// 使用 i0 和 i1 作为索引,从表中获取两个双精度浮点数,并将它们组合成一个双精度向量返回
return npyv_set_f64(table[i0], table[i1]);
}
// 使用给定的表和索引查找表中的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)); }
// 使用给定的表和索引查找表中的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)); }