Microsoft SEAL迭代器(iterator)分析

安全之安全(security²)博客目录导读

目录

@par PolyIter, RNSIter 和 CoeffIter

@par PtrIter 和 StrideIter

@par IterTuple

@par 常见的 PtrIter 类型的别名

@par 创建 SEAL 迭代器

@par 使用 ReverseIter 反转方向

@par SEAL_ITERATE

@par 编码约定

@par 常见函数的迭代器重载

@par 使用 SeqIter 进行索引

@par 关于分配的注意事项

@par 指向临时分配的迭代器


@par PolyIter, RNSIter 和 CoeffIter

在这个文件中,我们定义了一组自定义迭代器类(“SEAL 迭代器”),用于在 Microsoft SEAL 中更方便地遍历密文多项式、它们的 RNS 组件以及 RNS 组件中的系数。所有 SEAL 迭代器都满足 C++ LegacyRandomAccessIterator 的要求。SEAL 迭代器非常适合与 SEAL_ITERATE 宏一起使用,该宏在 C++17 中扩展为 std::for_each_n,在 C++14 中扩展为 seal::util::seal_for_each_n。所有 SEAL 迭代器都派生自 SEALIterBase。

最重要的 SEAL 迭代器类的行为如下图所示:

+-------------------+
|    指针和大小     |  构造  +-----------------+
| 或密文            |------>| (Const)PolyIter |  遍历密文中的 RNS 多项式
+-------------------+       +--------+--------+  (coeff_modulus_size 个 RNS 组件)||| 解引用||v+----------------+  构造  +----------------+| 指针和大小     |------>| (Const)RNSIter |  遍历 RNS 多项式中的 RNS 组件+----------------+       +-------+--------+  (poly_modulus_degree 个系数)||| 解引用||v+----------------+  构造  +------------------+| 指针和大小     |------>| (Const)CoeffIter |  遍历单个 RNS 多项式组件中的系数 (std::uint64_t)+----------------+       +---------+--------+||| 解引用||v+-------------------------+| (const) std::uint64_t & |+-------------------------+

@par PtrIter 和 StrideIter

PtrIter<T *> 和 StrideIter<T *> 都是模板化的 SEAL 迭代器,它们包装了原始指针。这两者的区别在于,PtrIter<T *> 的步长总是1,而 StrideIter<T *> 的步长可以设置为任意值。CoeffIter 是 PtrIter<std::uint64_t *> 的 typedef,而 RNSIter 与 StrideIter<std::uint64_t *> 几乎相同,但仍然是不同的类型。

+----------+  构造  +-------------------+
| MyType * |------>| PtrIter<MyType *> |  原始指针的简单包装
+----------+       +----+----------+---+|          ||          |解引用       |          |     PtrIter<MyType *>::ptr()|          |  或隐式转换|          |v          v+----------+  +----------+| MyType & |  | MyType * |+----------+  +----------++----------+  构造  +----------------------+
| MyType * |------>| StrideIter<MyType *> |  带自定义步长的原始指针的简单包装
+----------+       +-----+----------+-----+|          ||          |解引用       |          |     StrideIter<MyType *>::ptr()|          |  或隐式转换|          |v          v+----------+  +----------+| MyType & |  | MyType * |+----------+  +----------+

@par IterTuple

一个非常有用的模板类是(可变参数的)IterTuple<...>,它允许多个 SEAL 迭代器一起使用。IterTuple 本身就是一个 SEAL 迭代器,并且在库中常常使用嵌套的 IterTuple 类型。解引用 IterTuple 总是返回一个 std::tuple,其中每个 IterTuple 元素都被解引用。由于 IterTuple 可以从一个包含每个迭代器的单参数构造函数参数的 std::tuple 构造,所以解引用后的 std::tuple 通常可以直接传递给期望 IterTuple 的函数。

IterTuple 的各个组件可以通过 seal::util::get<i>(...) 函数访问。IterTuple 的行为概述如下图所示:

 +-----------------------------------------+| IterTuple<PolyIter, RNSIter, CoeffIter> |+--------------------+--------------------+||| 解引用||v+--------------------------------------------------+| std::tuple<RNSIter, CoeffIter, std::uint64_t &>> |+------+-------------------+-------------------+---+|                   |                   ||                   |                   ||  std::get<0>      |  std::get<1>      |  std::get<2>|                   |                   ||                   |                   |v                   v                   v+-------------+    +---------------+   +-----------------+|   RNSIter   |    |   CoeffIter   |   | std::uint64_t & |+-------------+    +---------------+   +-----------------+

有时候我们需要使用多个嵌套的迭代器元组。在这种情况下,使用嵌套的 get<...> 调用来访问嵌套的迭代器会很繁琐。考虑以下情况,其中 encrypted1encrypted2Ciphertextsdestination 是一个 CiphertextPolyIter

IterTuple<PolyIter, PolyIter> I(encrypted1, encrypted2);
IterTuple<decltype(I), PolyIter> J(I, destination);
auto encrypted1_iter = get<0>(get<0>(J));
auto encrypted2_iter = get<1>(get<0>(J));

一种更简单的方法是使用另一种形式的 get<...>,它接受多个索引并以嵌套的方式访问结构。例如,在上述情况下,我们也可以这样写:

auto encrypted1_iter = get<0, 0>(J));
auto encrypted2_iter = get<0, 1>(J));

请注意,最内层元组的索引首先出现在列表中,即顺序与嵌套 get<...> 调用中出现的顺序相反。这种反转的原因是,当推断迭代器是什么时,首先检查最内层的范围,最后检查最外层的范围,这与索引的顺序相对应。我们还提供了类似的函数,用于访问嵌套的 std::tuple 对象,这在访问嵌套的 IterTuple 解引用时是必要的。

@par 常见的 PtrIter 类型的别名

很常见的类型是 PtrIter<Modulus *>PtrIter<NTTTables *>。为了简化表示,我们为这些类型设置了别名:ModulusIterNTTTablesIter。还有const版本 ConstModulusIterConstNTTTablesIter,分别包装指向常量 ModulusNTTTables 的指针。

@par 创建 SEAL 迭代器

使用可变参数的 iter 函数创建迭代器是最简单的,当给定一个或多个可以自然转换为 SEAL 迭代器的参数时,它会输出一个适当的迭代器或迭代器元组。再次考虑上面的代码片段,以及编写模板参数可能变得多么混乱。相反,我们可以简单地写:

auto I = iter(encrypted1, encrypted2);
auto J = iter(I, destination);
auto encrypted1_iter = get<0, 0>(J));
auto encrypted2_iter = get<0, 1>(J));

iter 函数创建 IterTuple三种方法。第一种方法是传递一个 IterTuple 作为输入,在这种情况下 iter 输出它的副本;没有理由这样做。第二种方法是传递一组可变参数构造函数参数iter输出一个由与给定构造函数参数兼容的 SEAL 迭代器组成的 IterTuple。第三种方法是传递一个包含一组可变参数构造函数参数的 std::tuple;行为与第二种方法相同。

@par 使用 ReverseIter 反转方向

除了上面描述的迭代器类型外,我们还提供了 ReverseIter<SEALIter>,它可以反转迭代方向ReverseIter<SEALIter> 解引用为与 SEALIter 相同的类型:例如,解引用 ReverseIter<RNSIter> 结果是 CoeffIter,而不是 ReverseIter<CoeffIter>

使用 reverse_iter 函数可以很容易地从给定的 SEAL 迭代器创建一个 ReverseIter。例如,reverse_iter(encrypted) 将返回一个 ReverseIter<PolyIter>,如果 encrypted 是一个 PolyIterCiphertext。当传递多个参数时,reverse_iter 返回一个适当的 ReverseIter<IterTuple<...>>。例如,如果 encrypted1encrypted2PolyIterCiphertext 对象,那么 reverse_iter(encrypted1, encrypted2) 返回 ReverseIter<IterTuple<PolyIter, PolyIter>>

@par SEAL_ITERATE

SEAL 迭代器是为了与 SEAL_ITERATE 宏一起使用而设计的,以迭代一定数量的步骤,并在每一步调用给定的 lambda 函数。在 C++17 中,SEAL_ITERATE 展开为 std::for_each_n,在 C++14 中,它展开为 seal::util::seal_for_each_n -- 一个自定义实现。例如,以下代码片段出现在 Evaluator::bfv_multiply 中:

SEAL_ITERATE(iter(encrypted1, encrypted1_q, encrypted1_Bsk),encrypted1_size,behz_extend_base_convert_to_ntt);

这里,使用 iter 函数创建了一个 IterTuple<PolyIter, PolyIter, PolyIter>;参数类型是 Ciphertextencrypted1)、PolyIterencrypted1_q)和 PolyIterencrypted1_Bsk)。迭代器前进 encrypted1_size 次,每次都调用 lambda 函数 behz_extend_base_convert_to_ntt,并解引用迭代器元组。lambda 函数如下开始:

auto behz_extend_base_convert_to_ntt = [&](auto I) {set_poly(get<0>(I), coeff_count, base_q_size, get<1>(I));ntt_negacyclic_harvey_lazy(get<1>(I), base_q_size, base_q_ntt_tables);...
});

这里,参数 I 的类型是 IterTuple<RNSIter, RNSIter, RNSIter>解引用后的。在 lambda 函数中,我们首先将 RNS 多项式从 get<0>(I)encrypted1)复制到 get<1>(I)encrypted1_q),并将其转换为 NTT 形式。我们使用 ntt_negacyclic_harvey_lazy 的一个重载,它接受 RNSIter、RNS 基的大小和 ConstNTTTablesIter 作为参数,并分别转换每个 RNS 组件。查看 seal/util/ntt.h,我们看到函数 ntt_negacyclic_harvey_lazy 再次使用了 SEAL_ITERATE 实现。具体来说,它包含以下内容:

SEAL_ITERATE(iter(operand, tables), coeff_modulus_size, [&](auto I) {ntt_negacyclic_harvey_lazy(get<0>(I), get<1>(I));
});

在这里,iter 输出一个 IterTuple<RNSIter, ConstNTTTablesIter>。在这种情况下,要调用的lambda函数是内联定义的。参数 I 取值 IterTuple<CoeffIter, const NTTTables *>,并且每一步调用 ntt_negacyclic_harvey_lazyCoeffIter 重载,带有对匹配的 NTTTables 对象的引用。

@par 编码约定

在上面的代码片段中,有两个重要的编码约定需要遵循:

  1. 使用 IJK 等作为表示 SEAL 迭代器的 lambda 函数参数。这简洁明了,并且使得在 SEAL 的其他上下文中不应使用这些变量名称,从而明确这些对象是 SEAL 迭代器

  2. 传递给 SEAL_ITERATE 的 lambda 函数几乎总是(参见第 3 点)接受类型为 auto 的参数。这将生成外观简单且性能良好的代码,达到预期的结果。

  3. 第 2 点的唯一例外是当 SEAL_ITERATE 操作单个 PtrIter<T *> 时:解引用返回一个 T &,这可能需要通过引用传递给 lambda 函数。有关示例,请参见 seal::util::ntt_negacyclic_harvey 中的 seal/util/ntt.h,其中 lambda 函数参数是 auto &

    注意:IterTuple<PolyIter, CoeffIter> 将解引用为 std::tuple<RNSIter, std::uint64_t &>,可以安全地按值传递给 lambda 函数。因此,lambda 函数中类型为 auto 的参数将很可能按预期工作。

    注意:另一种始终正确的方法是使用转发引用 auto && 作为 lambda 函数参数。然而,我们认为这会对代码进行不必要的复杂化,收益甚微。

@par 常见函数的迭代器重载

有些函数具有重载,直接接受CoeffIter、RNSIter或PolyIter输入,并将相关操作应用于迭代器所指示的整个结构。例如,函数seal::util::negate_poly_coeffmod可以对给定的模数取反单个RNS组件模(CoeffIter重载),对匹配的模数元素数组取反整个RNS多项式模(RNSIter重载),或对RNS多项式数组取反(PolyIter重载)。

@par 使用 SeqIter 进行索引

有时在 SEAL_ITERATE lambda 函数内部,知道迭代的索引是很方便的。这可以使用 SeqIter<T> 迭代器来完成。模板参数是索引计数器的算术类型。

创建 SeqIter 对象的最简单方法是使用 seq_iter 函数。例如,seq_iter(0) 返回一个初始值为 0 的 SeqIter<int> 对象。或者,iter 函数将检测传递给它的算术类型,并从中创建 SeqIter 对象。例如,调用 iter(0) 等效于调用 seq_iter(0),这也适用于 iter 的多参数调用。解引用一个 SeqIter 对象返回当前值。对于反向索引,只需将 SeqIter 包装到 ReverseIter 中,或直接调用带有起始索引的 reverse_iter

@par 关于分配的注意事项

未来我们希望使用 C++17 引入的并行版本 std::for_each_n。为了实现这一点,请注意在 lambda 函数中如何使用堆分配。特别是在繁重的 lambda 函数中,最好在 lambda 函数内部调用 seal::util::allocate 进行所需的任何分配,而不是使用从外部捕获的分配。

@par 指向临时分配的迭代器

在许多情况下,可能希望分配一个临时缓冲区并创建一个指向它的迭代器。但是,现在必须注意为分配和设置迭代器使用正确的大小参数。为此,我们提供了一些有用的宏,这些宏设置指针并仅向函数公开迭代器。例如,代替编写以下容易出错的代码:

auto temp_alloc(allocate_poly_array(count, poly_modulus_degree, coeff_modulus_size, pool));
PolyIter temp(temp_alloc.get(), poly_modulus_degree, coeff_modulus_size);

我们可以简单地写:

SEAL_ALLOCATE_GET_POLY_ITER(temp, count, poly_modulus_degree, coeff_modulus_size, pool);

然而,后者不公开分配本身的名称。还有类似的宏用于分配缓冲区并设置 PtrIter<T *>StrideIter<T *>RNSIterCoeffIter 对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/49694.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

动态接口调优:在Mojo模型中调整模型的输入输出接口

动态接口调优&#xff1a;在Mojo模型中调整模型的输入输出接口 在机器学习领域&#xff0c;Mojo模型通常指代一个经过训练、准备部署的模型。模型的输入输出接口&#xff08;I/O&#xff09;是模型与外界交互的桥梁&#xff0c;其设计直接影响到模型的可用性和灵活性。本文将探…

Linux权限维持篇

目录 SSH后门 &#xff08;1&#xff09;软链接sshd &#xff08;2&#xff09;SSH Key 生成公私钥 创建个authorized_keys文件来保存公钥 通过修改文件时间来隐藏authorized_keys &#xff08;3&#xff09;SSH Keylogger&#xff08;记录日志&#xff09; Linux的PA…

【Go系列】Go的UI框架Fyne

前言 总有人说Go语言是一门后端编程语言。 Go虽然能够很好地处理后端开发&#xff0c;但是者不代表它没有UI库&#xff0c;不能做GUI&#xff0c;我们一起来看看Go怎么来画UI吧。 正文 Go语言由于其简洁的语法、高效的性能和跨平台的编译能力&#xff0c;非常适合用于开发GUI…

MICA:面向复杂嵌入式系统的混合关键性部署框架

背景 在嵌入式场景中&#xff0c;虽然 Linux 已经得到了广泛应用&#xff0c;但并不能覆盖所有需求&#xff0c;例如高实时、高可靠、高安全的场合。这些场合往往是实时操作系统的用武之地。有些应用场景既需要 Linux 的管理能力、丰富的生态&#xff0c;又需要实时操作系统的高…

vue中scoped详解以及样式穿透>>>、/deep/、::v-deep

1、scoped scoped属性用于限制样式仅应用于当前组件。当一个style标签拥有scoped属性时&#xff0c;它的CSS样式就只能作用于当前的组件&#xff0c;通过该属性&#xff0c;可以使得组件之间的样式不互相污染。 原理&#xff1a;当样式中加了scoped属性时候&#xff0c;编译的…

数据库解析一维和二维简易JSON,

项目还在使用Oracle11&#xff0c;不支持后续官方的json解析方式&#xff0c; 在 前年、去年、今年 接连 遇到json解析问题后&#xff08;其实是公司的轮子效率太慢&#xff0c;太复杂&#xff0c;决定自己造个轮子&#xff0c;看看到底为什么慢&#xff0c;是不是真的很复杂&a…

【最新】cudnn安装教程

最近换了新电脑需要重新安装cuda和cudnn&#xff0c;发现现在cudnn的安装比以前方便多了&#xff0c;直接在官网下载exe安装包一键运行即可。安装的时候注意cuda和cudnn的对应关系即可&#xff1a;【最新】cuda和cudnn和显卡驱动的对应关系-CSDN博客 访问cudnn下载链接cuDNN 9…

Git 基础 GitHub【学习笔记】

一、Git 优势 大部分操作在本地完成&#xff0c;不需要联网完整性保证尽可能添加数据而不是删除或修改数据分支操作非常快捷流畅与 Linux 命令全面兼容 二、Git 程序安装 https://git-scm.com 三、Git 结构 #mermaid-svg-9Go6R1leWXWrDCqn {font-family:"trebuchet ms&quo…

Redis面试三道题目

针对Redis的面试题&#xff0c;我将从简单到困难给出三道题目&#xff0c;并附上参考答案的概要。 1. 简单题&#xff1a;请简述Redis是什么&#xff0c;以及它的主要优点。 参考答案&#xff1a; Redis简介&#xff1a;Redis是一个开源的、使用ANSI C语言编写、支持网络、可…

TCP/IP and Ethernet

目录 1. What is Internet?2. Ethernet2.1. Physical Layer(PHY)2.2. MACMAC帧格式MAC地址与IP地址2.3. RGMII接口3. TCP/IP3.1. Network Layer3.1.1. ARPARP工作过程ARP帧格式3.1.2. IP3.2. Transport Layer3.2.1. ICMP3.2.2. UDP3.2.3. TCP手把手教你学达芬奇&达芬奇Pro…

C#中GridControl的数据源双向绑定

1. 什么是双向数据绑定&#xff1f; 双向数据绑定是一种允许我们创建持久连接的技术&#xff0c;使模型数据和用户界面(UI)之间的交互能够自动同步。这意味着当模型数据发生变化时&#xff0c;UI会自动更新&#xff0c;反之亦然。这种双向数据绑定极大地简化了UI和模型数据之间…

C++STL简介(二)

目录 1.模拟实现string 1.string基本属性和大体框架 2.基本函数 2.1size&#xff08;&#xff09; 2.2 [] 2.3 begin() 和end() 2.4capacity&#xff08;&#xff09; 2.5 reserve 2.6push_back 2.7 append 2.8 2.9insert 2.10find 2.11substr 2.12 2.12 < …

运维锅总详解NFS

NFS是什么&#xff1f;如何对NFS进行部署及优化&#xff1f;NFS工作流程是什么&#xff1f;NFS的性能及优缺点是什么&#xff1f;NFS发展历史又是怎样的&#xff1f;希望本文能帮您解答这些疑惑&#xff01; 一、NFS简介 NFS (Network File System) 是由 Sun Microsystems 在…

论文精读(保姆级解析)—— Flash Diffusion

0 前言 今天分析的论文是《Flash Diffusion: Accelerating Any Conditional Diffusion Model for Few Steps Image Generation》。该论文发表在2024年&#xff0c;目前已开源在arxiv上&#xff0c;主要提出了一种高效、快速且多功能的蒸馏方法&#xff0c;用于加速预训练扩散模…

[C++][STL源码剖析] 详解AVL树的实现

目录 1.概念 2.实现 2.1 初始化 2.2 插入 2.2.1 旋转&#xff08;重点&#xff09; 左单旋 右单旋 双旋 2.❗ 双旋后&#xff0c;对平衡因子的处理 2.3 判断测试 完整代码&#xff1a; 拓展&#xff1a;删除 1.概念 二叉搜索树虽可以缩短查找的效率&#xff0c;但…

遇到Websocket就不会测了?别慌,学会这个Jmeter插件轻松解决....

websocket 是一种双向通信协议&#xff0c;在建立连接后&#xff0c;websocket服务端和客户端都能主动向对方发送或者接收数据&#xff0c;而在http协议中&#xff0c;一个request只能有一个response&#xff0c;而且这个response也是被动的&#xff0c;不能主动发起。 websoc…

【研路导航】保研英语面试高分攻略,助你一路过关斩将

面试攻略之 千锤百炼英语口语 写在前面 在保研面试中&#xff0c;英语口语往往是让许多同学感到头疼的一部分。如何在面试中展现出自信和流利的英语表达能力&#xff0c;是我们今天要探讨的主题。以下是一些有效的英语口语练习方法和常见题型解析&#xff0c;帮助你在保研面试…

Redis 7.x 系列【31】LUA 脚本

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Redis 版本 7.2.5 源码地址&#xff1a;https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 概述2. 常用命令2.1 EVAL2.2 SCRIPT LOAD2.3 EVALSHA2.4 SCRIPT FLUSH2.5 其他 3. …

LoRA:低秩自适应

LoRA:低秩自适应 本章节是对轻松上手微调大语言模型——QLORA篇中提到的LoRA的原理解释。 背后动机 现今模型的参数量变得越来越大&#xff0c;对预训练模型进行全微调变得越来越不可行。为了解决这个问题有了LoRA&#xff08;Low-Rank Adaption&#xff09;的诞生。将可训练…

linux命令行登录百度网盘下载文件

1、linux系统中已安装有python环境&#xff0c;安装bypy库和requests库安装方法如下&#xff1a; pip install requests -i https://pypi.doubanio.com/simple pip install bypy -i https://pypi.doubanio.com/simple # 这里我是直接使用如下命令安装的 rambop360:~$ sudo pip3…