2021SC@SDUSC
2021-12-19
第十二周完成事项
工作内容
本周的工作是继续分析剩余数系统的相关代码。首先回顾一下之前了解学习过的剩余数系统。
RNS——剩余数系统
与冗余数相反,剩余数表示系统(RNS,residue number system)是一种用较少的数表示较多的数的表示系统。RNS可显著提高信号处理应用中某些算法密集型场景的算法速度。此外,RNS也是研究快速算法极限理论的一个工具。
剩余数系统的作用是将原本比较大的数,使用多个相对小的数进行表示,这样在运算的时候,可以不必对原本比较大的数进行运算,对多个相对小的数进行运算可以加快运算速度。
代码分析
承接上周的工作进度,上周主要分析了RNSbase类的相关内容,包括RNSbase类的构造函数、复制构造函数、赋值构造函数以及一些对于类对象的判断修改的函数,本周将继续分析剩余数系统剩下的两个类之一,BaseConverter类。BaseConverter类的作用是进行RNSbase的转化。
首先我们来看一下BaseConverter类的全貌。
class BaseConverter
{
public:
BaseConverter(const RNSBase &ibase, const RNSBase &obase, MemoryPoolHandle pool)
: pool_(std::move(pool)), ibase_(ibase, pool_), obase_(obase, pool_)
{
if (!pool_)
{
throw std::invalid_argument("pool is uninitialized");
}
initialize();
}
SEAL_NODISCARD inline std::size_t ibase_size() const noexcept
{
return ibase_.size();
}
SEAL_NODISCARD inline std::size_t obase_size() const noexcept
{
return obase_.size();
}
SEAL_NODISCARD inline const RNSBase &ibase() const noexcept
{
return ibase_;
}
SEAL_NODISCARD inline const RNSBase &obase() const noexcept
{
return obase_;
}
void fast_convert(ConstCoeffIter in, CoeffIter out, MemoryPoolHandle pool) const;
oid fast_convert_array(ConstRNSIter in, RNSIter out, MemoryPoolHandle pool) const;
private:
BaseConverter(const BaseConverter ©) = delete;
BaseConverter(BaseConverter &&source) = delete;
BaseConverter &operator=(const BaseConverter &assign) = delete;
BaseConverter &operator=(BaseConverter &&assign) = delete;
void initialize();
MemoryPoolHandle pool_;
RNSBase ibase_;
RNSBase obase_;
Pointer<Pointer<std::uint64_t>> base_change_matrix_;
};
先分析一下BaseConverter类中的私有变量。
MemoryPoolHandle pool_;
RNSBase ibase_;
RNSBase obase_;
Pointer<Pointer<std::uint64_t>> base_change_matrix_;
pool_是内存池的句柄,用于对内存进行操作;ibase和obase分别是输入RNSbase和输出RNSbase;最后一个base_change_matrix是由指针的嵌套构成二维矩阵数组,主要负责RNSbase的变化。
BaseConverter(const BaseConverter ©) = delete;
BaseConverter(BaseConverter &&source) = delete;
BaseConverter &operator=(const BaseConverter &assign) = delete;
BaseConverter &operator=(BaseConverter &&assign) = delete;
上面的分别是复制构造函数和赋值构造函数,用delete进行修饰,表示表示这个函数被定义为deleted,也就意味着这个成员函数不能再被调用,否则就会出错。
void BaseConverter::initialize()
{
// Verify that the size is not too large
if (!product_fits_in(ibase_.size(), obase_.size()))
{
throw logic_error("invalid parameters");
}
// Create the base-change matrix rows
base_change_matrix_ = allocate<Pointer<uint64_t>>(obase_.size(), pool_);
SEAL_ITERATE(iter(base_change_matrix_, obase_.base()), obase_.size(), [&](auto I) {
// Create the base-change matrix columns
get<0>(I) = allocate_uint(ibase_.size(), pool_);
StrideIter<const uint64_t *> ibase_punctured_prod_array(ibase_.punctured_prod_array(), ibase_.size());
SEAL_ITERATE(iter(get<0>(I), ibase_punctured_prod_array), ibase_.size(), [&](auto J) {
// Base-change matrix contains the punctured products of ibase elements modulo the obase
get<0>(J) = modulo_uint(get<1>(J), ibase_.size(), get<1>(I));
});
});
}
接下来是BaseConverter类中的initialize函数,表示BaseConverter类的初始化。首先判断输入和输出的RNSbase变量大小是否匹配,不匹配会抛出异常。然后通过输出RNSbase变量和内存池句柄生成RNSbase的转换矩阵的行。接下来通过SEAL_ITERATE嵌套去生成RNSbase的转换矩阵的列。其中SEAL_ITERATE最后一项是定义了运算。
以上为BaseConverter类中private修饰的私有函数和变量,接下来继续分析使用public进行修饰的公开函数。
public:
BaseConverter(const RNSBase &ibase, const RNSBase &obase, MemoryPoolHandle pool)
: pool_(std::move(pool)), ibase_(ibase, pool_), obase_(obase, pool_)
{
if (!pool_)
{
throw std::invalid_argument("pool is uninitialized");
}
initialize();
}
首先是BaseConverter类的构造函数。先判断内存池句柄是否初始化,然后调用私有函数initialize进行初始化。
SEAL_NODISCARD inline std::size_t ibase_size() const noexcept
{
return ibase_.size();
}
SEAL_NODISCARD inline std::size_t obase_size() const noexcept
{
return obase_.size();
}
SEAL_NODISCARD inline const RNSBase &ibase() const noexcept
{
return ibase_;
}
SEAL_NODISCARD inline const RNSBase &obase() const noexcept
{
return obase_;
}
然后是四个get方法,获取私有变量及其大小。
void fast_convert(ConstCoeffIter in, CoeffIter out, MemoryPoolHandle pool) const;
oid fast_convert_array(ConstRNSIter in, RNSIter out, MemoryPoolHandle pool) const;
最后是两个转换函数,实现系数和RNSbase的快速转换。
先来看一下fast_convert函数。
void BaseConverter::fast_convert(ConstCoeffIter in, CoeffIter out, MemoryPoolHandle pool) const
{
size_t ibase_size = ibase_.size();
size_t obase_size = obase_.size();
SEAL_ALLOCATE_GET_COEFF_ITER(temp, ibase_size, pool);
SEAL_ITERATE(
iter(temp, in, ibase_.inv_punctured_prod_mod_base_array(), ibase_.base()), ibase_size,
[&](auto I) { get<0>(I) = multiply_uint_mod(get<1>(I), get<2>(I), get<3>(I)); });
// for (size_t j = 0; j < obase_size; j++)
SEAL_ITERATE(iter(out, base_change_matrix_, obase_.base()), obase_size, [&](auto I) {
get<0>(I) = dot_product_mod(temp, get<1>(I).get(), ibase_size, get<2>(I));
});
}
首先获取ibase和obase两个RNSbase变量的大小,然后通过ibase的大小和内存池句柄初始化SESL的系数迭代器,最后通过两轮SEAL迭代分别进行乘法模和点乘模去更新最终需要的out结果。
然后是fast_convert_array函数。
void BaseConverter::fast_convert_array(ConstRNSIter in, RNSIter out, MemoryPoolHandle pool) const
{
#ifdef SEAL_DEBUG
if (in.poly_modulus_degree() != out.poly_modulus_degree())
{
throw invalid_argument("in and out are incompatible");
}
#endif
size_t ibase_size = ibase_.size();
size_t obase_size = obase_.size();
size_t count = in.poly_modulus_degree();
// Note that the stride size is ibase_size
SEAL_ALLOCATE_GET_STRIDE_ITER(temp, uint64_t, count, ibase_size, pool);
SEAL_ITERATE(
iter(in, ibase_.inv_punctured_prod_mod_base_array(), ibase_.base(), size_t(0)), ibase_size,
[&](auto I) {
// The current ibase index
size_t ibase_index = get<3>(I);
if (get<1>(I).operand == 1)
{
// No multiplication needed
SEAL_ITERATE(iter(get<0>(I), temp), count, [&](auto J) {
// Reduce modulo ibase element
get<1>(J)[ibase_index] = barrett_reduce_64(get<0>(J), get<2>(I));
});
}
else
{
// Multiplication needed
SEAL_ITERATE(iter(get<0>(I), temp), count, [&](auto J) {
// Multiply coefficient of in with ibase_.inv_punctured_prod_mod_base_array_ element
get<1>(J)[ibase_index] = multiply_uint_mod(get<0>(J), get<1>(I), get<2>(I));
});
}
});
SEAL_ITERATE(iter(out, base_change_matrix_, obase_.base()), obase_size, [&](auto I) {
SEAL_ITERATE(iter(get<0>(I), temp), count, [&](auto J) {
// Compute the base conversion sum modulo obase element
get<0>(J) = dot_product_mod(get<1>(J), get<1>(I).get(), ibase_size, get<2>(I));
});
});
}
首先判断输入和输出的多项式模数是否相等,如果不等则抛出异常。
#ifdef SEAL_DEBUG
if (in.poly_modulus_degree() != out.poly_modulus_degree())
{
throw invalid_argument("in and out are incompatible");
}
#endif
然后获取ibase和obase的大小以及in的多项式模数,通过in的多项式模数、ibase_size和内存池句柄去初始化SEAL的步伐迭代器。
size_t ibase_size = ibase_.size();
size_t obase_size = obase_.size();
size_t count = in.poly_modulus_degree();
// Note that the stride size is ibase_size
SEAL_ALLOCATE_GET_STRIDE_ITER(temp, uint64_t, count, ibase_size, pool);
最后分别通过(in,ibase)和(out,base_change_matrix,obase)进行SEAL_ITERATE嵌套迭代完成最后的更新。
SEAL_ITERATE(
iter(in, ibase_.inv_punctured_prod_mod_base_array(), ibase_.base(), size_t(0)), ibase_size,
[&](auto I) {
// The current ibase index
size_t ibase_index = get<3>(I);
if (get<1>(I).operand == 1)
{
// No multiplication needed
SEAL_ITERATE(iter(get<0>(I), temp), count, [&](auto J) {
// Reduce modulo ibase element
get<1>(J)[ibase_index] = barrett_reduce_64(get<0>(J), get<2>(I));
});
}
else
{
// Multiplication needed
SEAL_ITERATE(iter(get<0>(I), temp), count, [&](auto J) {
// Multiply coefficient of in with ibase_.inv_punctured_prod_mod_base_array_ element
get<1>(J)[ibase_index] = multiply_uint_mod(get<0>(J), get<1>(I), get<2>(I));
});
}
});
SEAL_ITERATE(iter(out, base_change_matrix_, obase_.base()), obase_size, [&](auto I) {
SEAL_ITERATE(iter(get<0>(I), temp), count, [&](auto J) {
// Compute the base conversion sum modulo obase element
get<0>(J) = dot_product_mod(get<1>(J), get<1>(I).get(), ibase_size, get<2>(I));
});
});
最后可以说明一下,这里的SEAL_ITERATE迭代器与我们熟知的for循环时一样的,区别不过是通过最后的[&](auto I)规定一种迭代的运算,所以没有在过多的展开对于迭代操作的分析。
总结
本周基本完成了对于剩余数系统的分析,下一周将对RNS进行扫尾以及总结这一学期的所学内容。
通过学习和分析RNS,明白了如何加快运算速度。目前软件工程应用与实践这门课程也逐渐进入尾声,通过这一学期的学习,掌握了如何对于一门新的方向进行着手,为以后的发展奠定了基础。
最后,感谢孔老师的指导,感谢戴老师和其他审核老师的阅读!