MemoryView
MemoryView 提供了在扩展库之间共享内存中固定大小元素的多维同构数组的功能。
免责声明
-
此功能仍处于实验阶段。此处描述的规范可能会在未来发生更改。
-
本文档正在编写中。请参考 Ruby 的主分支以获取本文档的最新版本。
概览
我们有时会处理这样一些对象,它们在其内部表示中,将具有相同类型固定大小元素存储在连续内存区域的数组。numo-narray 中的 Numo::NArray 和 rmagick 中的 Magick::Image 是这类对象的典型示例。MemoryView 充当了一个中心枢纽,可以在此类库之间共享这些对象的内部数据,而无需复制。
在数据分析、机器学习和图像处理等领域,数据的无复制共享非常重要。在这些领域,人们需要使用多个库来处理大量内存中的数据。如果我们被迫在交换大型数据时进行复制,那么大量数据处理时间将被复制数据所占据。使用 MemoryView 可以避免这种时间浪费。
MemoryView 有两类 API
-
生产者 API
类可以注册自己的 MemoryView 条目,允许这些类的对象暴露其 MemoryView。
-
消费者 API
消费者 API 允许我们获取和管理对象的 MemoryView。
MemoryView 结构
MemoryView 结构 rb_memory_view_t 用于导出对象的 MemoryView。此结构包含对象(MemoryView 的所有者)的引用、导出内存头部的指针以及描述内存结构的元数据。元数据可以描述带跨度的多维数组。
MemoryView 结构成员
MemoryView 结构由以下成员组成。
-
VALUE obj对通过 MemoryView 导出内存的原始对象的引用。
RubyVM管理 MemoryView 导出对象的引用计数,以保护它们免受垃圾回收。消费者无需费力保护此对象免受GC的影响。 -
void *data指向导出内存头部的指针。
-
ssize_t byte_sizedata指向的内存中的字节数。 -
bool readonly只读内存为
true,可写内存为false。 -
const char *format描述元素格式的字符串,或
NULL表示无符号字节。 -
ssize_t item_size每个元素的字节数。
-
const rb_memory_view_item_component_t *item_desc.components元素中组件元数据数组。
-
size_t item_desc.lengthitem_desc.components中的元素数量。 -
ssize_t ndim维数。
-
const ssize_t *shape一个
ndim大小的数组,指示每个维度中的元素数量。当ndim为 1 时,此值可以为NULL。 -
const ssize_t *strides一个
ndim大小的数组,指示跳到每个维度中的下一个元素需要跳过的字节数。当ndim为 1 时,此值可以为NULL。 -
const ssize_t *sub_offsets一个
ndim大小的数组,包含在 MemoryView 导出嵌套数组时每个维度的偏移量。当 MemoryView 导出扁平数组时,此值可以为NULL。 -
void *private_dataMemoryView 提供者内部使用的私有数据。当不需要任何私有数据时,此值可以为
NULL。
MemoryView API
面向消费者
-
bool rb_memory_view_available_p(VALUE obj)如果
obj支持导出 MemoryView,则返回true。否则返回false。如果此函数返回
true,并不意味着rb_memory_view_get函数会成功。 -
bool rb_memory_view_get(VALUE obj, rb_memory_view_t *view, int flags)如果给定的
obj支持导出符合给定flags的 MemoryView,则此函数将用 MemoryView 的信息填充view并返回true。在这种情况下,obj的引用计数会增加。如果
obj和flags的组合无法导出 MemoryView,则此函数返回false。在这种情况下,view的内容不会被触及。当不再需要 MemoryView 时,必须通过
rb_memory_view_release来释放导出的 MemoryView。 -
bool rb_memory_view_release(rb_memory_view_t *view)释放给定的 MemoryView
view并减少view->obj的引用计数。消费者必须在不再需要 MemoryView 时调用此函数。未能调用此函数将导致内存泄漏。
-
ssize_t rb_memory_view_item_size_from_format(const char *format, const char **err)计算元素所占用的字节数。
当计算失败时,
format中失败的位置会存储到err中,并返回-1。 -
void *rb_memory_view_get_item_pointer(rb_memory_view_t *view, const ssize_t *indices)计算由给定
indices指示的项的位置。indices的长度必须等于view->ndim。此函数在需要时初始化view->item_desc。 -
VALUE rb_memory_view_get_item(rb_memory_view_t *view, const ssize_t *indices)返回由给定
indices指示的项的 Ruby 对象表示。indices的长度必须等于view->ndim。此函数使用rb_memory_view_get_item_pointer。 -
rb_memory_view_init_as_byte_array(rb_memory_view_t *view, VALUE obj, void *data, const ssize_t len, const bool readonly)
将 view 的成员初始化为一维字节数组。
-
void rb_memory_view_fill_contiguous_strides(const ssize_t ndim, const ssize_t item_size, const ssize_t *const shape, const bool row_major_p, ssize_t *const strides)
使用给定形状和元素大小的连续数组的字节跨度填充 strides 数组。
-
void rb_memory_view_prepare_item_desc(rb_memory_view_t *view)
填充 view 的 item_desc 成员。
-
bool rb_memory_view_is_contiguous(const rb_memory_view_t *view)
如果 MemoryView view 中的数据是行主序或列主序连续的,则返回 true。
否则返回 false。
-
bool rb_memory_view_is_row_major_contiguous(const rb_memory_view_t *view)
如果 MemoryView view 中的数据是行主序连续的,则返回 true。
否则返回 false。
-
bool rb_memory_view_is_column_major_contiguous(const rb_memory_view_t *view)
如果 MemoryView view 中的数据是列主序连续的,则返回 true。
否则返回 false。