当前位置: 首页 > news >正文

RL | 速读 IJCAI 的强化学习论文

闲干谋伟swizzle 语法

经常编写着色器的同学应该对 swizzle(重排)语法非常熟悉,方便又灵活,可以说是用过一次便回味无穷。

代码

vec4 color = vec4(1.0, 0.5, 0.0, 1.0);

vec3 rgb = color.rgb; // { 1.0, 0.5, 0.0 }

vec2 xy = color.xy; // { 1.0, 0.5 }

vec4 bgra = color.bgra; // { 0.0, 0.5, 1.0, 1.0 }

可惜的是,C++ 中并不存在这样的语法,但是可以利用语法特性来模拟它,基本的思路是使用一个代理类来存储被操作点的引用以及需要操作的位置信息。

知名的 swizzle 实现

GLM

作为图形编程中的常客,GLM 提供了一套和 GLSL 相似的 swizzle 语法,只需要在使用前定义宏 GLM_FORCE_SWIZZLE 即可在向量类中使用了:

代码

#define GLM_FORCE_SWIZZLE

#include

glm::vec3 v{1.0f, 2.0f, 3.0f};

v.xy = v.yz;

glm::vec3 reverse = v.zyx;

GLM 的实现方式是在类的未命名 union 内部定义一系列预定义的 swizzle 组合代理类,这些类只存储一个标记 vec 类内存起始位置的 char _buffer[1],而需要操作的位置信息则以模板参数形式编译进类型信息本身。

当一个 vec 类被构造时,这些代理类的 _buffer 即被初始化为 vec 实例的内存起始位置,当需要访问代理类的数据时,将 _buffer 转换为 vec 实例化时的数值类型指针,再取出位置信息作为索引即可实现对 vec 数据进行特定模式的访问。

GLM 的 swizzle 实现可以说是非常优雅,在形式和作用上是最还原 GLSL swizzle 语法的。

然而这种实现方式有一个缺点:所有的 swizzle 组合都是预定义的。GLM 的 vec 支持 2,3,4 维度的 swizzle,以 glm::vec3 来举例,它有 3 个元素,则能够组成的 swizzle 组合的总数为:

也就是说在 glm::vec3 的类定义中会有 117 个类似于 xx, xy, xxx, xyz, xxxx, zyzw 这样的成员(位于未命名 union 内)。虽然它们共用同一块内存,不会增加类的大小,但是代码编辑器的智能补全会将它们一一列举出来,这会让其他的成员变量、函数淹没在这些符号之间,体验上多少有点不好:

注意到 GLM 的绝大多数向量的计算操作都是使用外部函数例如 glm::normalize(v); 而没有将它们写成成员函数,是否也跟这个问题有点关系?

Eigen

Eigen 并没有直接提供 swizzle 语法,但是它的 IndexedView 提供类似的功能:

代码

Eigen::Vector3f v{ 1.0f, 2.0f, 3.0f };

//swz 类型是 Eigen::IndexedView, Eigen::internal::SingleRange<0>>

auto swz = v({1, 0});

swz = Eigen::Vector2f{4.0f, 5.0f};

v({0, 1, 2}) = v({2, 0, 1}); //相当于 v.xyz = v.zxy

我没有细看 Eigen 的源码,但是表面上猜测,IndexedView 类的实现思路基本上也是一种代理的思想,并且它应该将绑定数据的引用和位置信息都保存在了类的数据成员中:

代码

Eigen::Vector3f v{ 1.0f, 2.0f, 3.0f };

constexpr int swz2_size = sizeof(v({0, 1})); //24 byte

constexpr int swz3_size = sizeof(v({0, 1, 2})); //24 byte

constexpr int swz4_size = sizeof(v({0, 0, 1, 2})); //48 byte

constexpr int swz5_size = sizeof(v({0, 0, 1, 1, 2})); //32 byte

constexpr int swz6_size = sizeof(v({0, 0, 1, 1, 2, 2})); //40 byte

不同长度的 IndexedView 类的大小是不同的,这说明 IndexedView 类确实将位置信息也保存成为了数据成员。可以看到不同的长度对应的类大小增长很符合 8 字节对齐的特征,但有趣的是长度为 4 时比较反常,经过我的实验,长度为 4 的倍数的 IndexedView 的大小都比较反常,估计是 Eigen 内部的针对性优化导致的。

总的来说,Eigen 的 IndexedView 完全可以满足 swizzle 的功能,但它的主要目标是通用和高效,没有必要为特定的语法作封装。

我的实现

我在编写 point 类时并不知道 GLM 的 swizzle 模块,更不知道 Eigen 的 IndexedView,但是最终实现出来的代码用的思路都相同:用一个代理类作为中间层来进行数据的间接访问。

代理类 exchanger 将位置编译进类型信息中,并保存一个操作数据对象的指针,通过自定义的赋值运算符和类型转换运算符与其他的数据类型进行数据交换,逻辑相当简单。

为了实现使用 xyzw, rgba, stpq 这些标签指定位置信息,我借用 boost preprocessor 库修改每个 swizzle 函数的调用参数为它对应的 index。对于左值操作对象,swizzle 返回一个 exchanger(或 const_exchanger) 类的实例,而如果操作对象是右值,则直接返回一个对应长度的 point 实例(数据拷贝而非引用,避免野指针)。

最终实现的 point 类支持任意长度(实际受限于 boost preprocessor 和编译器限制)和任意位置(代码中支持 0-255,但可以通过在 point_swizzler.hpp 的 POINT_SWIZZLE_CONVERT_PREFIX_255 之后继续添加条目支持更多的位置)的 swizzle.

代码在这里,可以在 test.cpp 中查看使用示例,目前只是提供一种 swizzle 的实现,尚未经过严格测试。

总结

在实现完 point 类之后,我才发现 GLM 和 Eigen 中的类似功能实现,又是一次重复造轮子。但是还是颇有收获的,想当初入门 C++ 时看到模板代码就头疼,现在不管多复杂的库代码也能慢慢剖开分析实现思路,其中很多技巧都是在一次次造轮子中深入掌握的。

http://www.jsqmd.com/news/468639/

相关文章:

  • MATLAB插床导杆机构运动学动力学分析:包含Simulink模型、运动仿真及说明书
  • C#与Halcon联合编程实现视觉几何定位与测量框架:从入门到精通,支持圆ROI拟合、标定及模板管理
  • 程序员空窗期如何解决?
  • 《C++实战项目-高并发内存池》2.ObjectPool构造
  • 【OpenGL ES】在Android上手撕一个mini版的渲染框架
  • NOR Flash和NAND flash有什么区别
  • 拆解美团UV量提高的底层逻辑:从流量获取到留存的全链路优化
  • 三年独立开发,我从中学到什么?
  • KEIL5中文件中的字都是黑色的
  • python包发布流程
  • Android系统卡顿排查实战:当system_server卡在io_schedule(二)
  • 【OpenClaw】彻底卸载教程:告别残留,3步搞定(附一键脚本+检测工具)
  • 安静不下来?如何应对课堂注意力不集中与冲动行为调整策略
  • 保安管理公司切入物业赛道的数字化路径与策略
  • 数字化运维实践:如何构建全场景、智能化的设备管理系统?
  • PyTorch 实现这个张量逐元素乘积的计算
  • 2026青岛子宫腺肌症医院 青岛新阳光妇产医院本地优选
  • 教你卸载 OpenClaw,附详细教程
  • 鸿蒙 HarmonyOS 6 | 多媒体(05)全局播控 AVSession 接入与后台控制
  • 2026年最值得学习的10个 Python AI库(程序员必备)
  • 风味优化/异味溯源 | 一站式GC-MS挥发性成分检测解决方案
  • Tiger_Rory
  • 红外测温发射率修正完全技术指南
  • 基于Simulink 2021A以上版本的混合储能系统下垂控制策略下的光储微网直流母线电压稳定性研究
  • 安全使用OpenClaw:从官方威胁模型出发,全面规避AI智能体风险
  • ADHD儿童运动疗法是什么?对注意力障碍的改善有何帮助?
  • 有没有小红书多账号管理工具?小红书私信聚合管理
  • MATLAB仿真平台实现储能参与调峰调频联合优化模型:超线性增益下的充放电策略优化与联合运行探索
  • 蓝桥杯备赛3.11
  • 通达信【超准主力专用抄底】副图与选股指标CJM99分享