将 Arrow 数据从 Node.js 传递到 Rust,无需复制

los*_*aut 5 node.js rust apache-arrow rust-arrow2

使用 Apache Arrow 格式将数据从 Node.js 传递到 Rust 的最佳方法是什么?用每种语言存储数据很容易,但共享内存给我带来了挑战。

我正在使用Napi-rs生成 node.js API 绑定。

对于下面的 JavaScript 代码,我收到“无法从缓冲区创建引用”的消息。当我尝试传递arrowVector.data[0].buffers给 rust 函数时,我得到 "../src/node_buffer.cc:245:char *node::Buffer::Data(Local<v8::Value>): Assertion `val->IsArrayBufferView() ' 失败的。”

我想我在这里缺少一些核心内容。

这是我的示例节点测试代码:

import { makeVector } from 'apache-arrow';
import {testFn} from './index.js';

// Create arrow Vec
const LENGTH = 2000;
const rainAmounts = Float32Array.from(
    { length: LENGTH },
    () => Number((Math.random() * 20).toFixed(1)));

const arrowVector = makeVector(rainAmounts);

// how to get buffers from vec? and send to rust function

testFn(arrowVector);
Run Code Online (Sandbox Code Playgroud)

这是我的 Rust 代码示例:

use napi::bindgen_prelude::Buffer;

#[napi]
pub fn test_fn(buffers: Buffer) {
    println!("test_fn called");
}
Run Code Online (Sandbox Code Playgroud)

Von*_*onC 5

NAPI-RS确实提到:

BufferRust 和 Node.js 之间通过和进行零拷贝数据交互TypedArray

但是...您直接传递给 Rust 函数的不是缓冲区
它是一个箭头向量

testFn(arrowVector);
Run Code Online (Sandbox Code Playgroud)

这与 Rust 方面不兼容:

#[napi]
pub fn test_fn(buffers: Buffer) {
    println!("test_fn called");
}
Run Code Online (Sandbox Code Playgroud)

因此,出现Failed to create reference from Buffer错误消息。

在 Node.js 代码中,获取 Arrow 向量的缓冲区并将它们直接传递给 Rust。

import { makeVector } from 'apache-arrow';
import {testFn} from './index.js';

// Create arrow Vec
const LENGTH = 2000;
const rainAmounts = Float32Array.from(
    { length: LENGTH },
    () => Number((Math.random() * 20).toFixed(1))
);

const arrowVector = makeVector(rainAmounts);

// Get buffers from the Arrow vector and send to Rust
testFn(arrowVector.data.buffers[0].buffer);
Run Code Online (Sandbox Code Playgroud)

在 Rust 中,Napi-rs 缓冲区应该能够直接从 Node.js 获取缓冲区。

use napi::{CallContext, Env, JsObject, Result, Task};
use napi_derive::napi;

#[napi]
fn test_fn(ctx: CallContext) -> Result<JsObject> {
    let buffer: Buffer<u8> = ctx.get::<Buffer<u8>>(0)?;
    
    // Here, buffer.data() gives you a slice to the actual data

    println!("test_fn called with buffer of length {}", buffer.length());

    ctx.env.get_undefined()
}
Run Code Online (Sandbox Code Playgroud)

但是,请记住,箭头向量通常包含多个缓冲区。根据所涉及的数据类型,您可能需要将多个缓冲区从 Node.js 传递到 Rust 并在 Rust 中正确解释它们。
apache/arrow/js/src/data.ts#buffers()

public get buffers() {
    return [this.valueOffsets, this.values, this.nullBitmap, this.typeIds] as Buffers<T>;
}
Run Code Online (Sandbox Code Playgroud)

这种方法可以让您避免复制数据,但内存仍然由 Node.js 管理,因此您需要小心生命周期。如果 Node.js 垃圾收集原始 Arrow 向量,则传递给 Rust 的缓冲区数据可能会被释放。为了避免这种情况,请确保只要 Rust 代码可以访问其数据,Node.js 中的箭头向量就保持在范围内并处于活动状态。
有关更多信息,请参阅“变量在 Node.js 中在内存中保留多长时间”。