使用 webassembly 在浏览器中查询大型数据集

Posted

技术标签:

【中文标题】使用 webassembly 在浏览器中查询大型数据集【英文标题】:Querying a large dataset in-browser using webassembly 【发布时间】:2021-09-20 22:57:54 【问题描述】:

为了论证,假设浏览器允许在 WebAssembly 应用程序中使用 4GB of memory。忽略压缩和其他数据存储考虑,如果用户有一个 3GB 的本地 csv 文件,我们可以使用 webassembly(当然也可以是 javascript)在内存中查询该数据。例如,如果用户的数据格式如下:

ID Country Amount
1 US 12
2 GB 11
3 DE 7

然后在几行代码中,我们可以做一个基本算法来过滤到ID=2,即SELECT * FROM table WHERE id=2的SQL等价物。

现在,我的问题是,是否有可能在 任何 浏览器中(并且可能选择了实验性标志和/或某些用户偏好),以便可以对不适合的文件进行查询内存,即使正确压缩。比如in this blog post,一个~500GB的文件被加载然后查询。我知道 500GB 的数据并没有完全加载到内存中,并且可能存在面向列的数据结构,因此只需要读取某些列,但无论哪种方式,操作系统都可以访问文件系统,因此文件比可以使用可用内存。

这是否可以在 WebAssembly 浏览器应用程序中以任何方式进行?如果是这样,它可以如何完成的大纲是什么?我知道这个问题可能需要一些研究,所以当它可用于赏金时,我可以添加 500 点赏金来鼓励答案。 (请注意,使用的底层语言是 C++-compiled-to-wasm,但我认为这对于这个问题并不重要。)

我想一种可能性可能类似于:https://rreverser.com/webassembly-shell-with-a-real-filesystem-access-in-a-browser/。

【问题讨论】:

这篇文章被标记为 [rust],但如果你的底层语言是 C++,那么使用 Rust crates 和 Rust wasm bindings 对你有价值吗? @kmdreko 很好,C++ 或 Rust 都可以。 I know that the 500GB of data is not loaded entirely in memory, and there's probably a column-oriented data structure so that only certain columns need to be read, - 由于您不需要阅读整个文件,究竟是什么问题? 虽然我知道这并不令人满意,但我认为这样做真的没有多大意义。在浏览器中的 WASM 中,您与文件交互的唯一方法是将浏览器功能导入 WASM 并使用现有的浏览器功能。在这种情况下,这可能是FileReaderBlob。由于这些 API 是异步的,因此您还需要导出/导入一堆 Promise API。最后,您将为在 JS 中更简单的事情做大量的编组工作。如果 WASM 有自己的文件 API,情况可能会有所不同... 参见***.com/questions/51047146/… 使用来自 WASM 的 FileReader(通过 Rust)。 【参考方案1】:

Javascript 文件 API

通过研究File API,事实证明,当读取文件时,浏览器将始终处理您一个Blob。这给人的印象是浏览器将所有文件都提取到内存。 Blob 也有一个 .stream() 函数,它返回一个 ReadableStream 以流式传输相同的 Blob

事实证明(至少在Chrome 中)处理的Blob 是虚拟的,并且在请求之前不会加载底层文件。文件对象切片和实例化读取器都不会加载整个文件:

file.slice(file.size - 100)
(await reader.read()).value.slice(0, 100)

Here is a test Sandboxand the sourcecode

该示例让您选择文件广告将显示最后 100 个字符(使用 .slice())和前 100 个使用 ReadableStream(请注意,流函数没有 seek 功能)

我已经测试了高达 10GB(最大的.csv),并且浏览器不会消耗任何 RAM

这回答了问题的第一部分。由于能够在不消耗 RAM 的情况下流式传输(或执行分块访问)文件,您可以使用任意大的文件并搜索您的内容(二进制搜索或表扫描)。

Web 程序集

在使用stdwebRust 中没有.read() 功能(因此无法流式传输内容)。但是File 确实有.slice() 函数来切片底层的blob(与javascript 相同)。这是一个最小的工作示例:

#[macro_use]
extern crate stdweb;

use stdweb::js_export;

use std::convert::From;
use stdweb::web::IBlob;
use stdweb::web::File;
use stdweb::web::FileReader;
use stdweb::web::FileReaderResult;

#[js_export]
fn read_file(file: File) 
    let blob = file.slice(..2048);
    let len = stdweb::Number::from(blob.len() as f64);

    js! 
        var _len = @len;
        console.log("length=" + _len);
        var _blob = @blob;
        console.log(_blob);
    


fn main() 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WASM</title>
</head>
<body>
    <input type="file" id="field" />

    <script src="the_compiled_wasm_binding.js"></script>
    <script>
        async function onChange(e) 
            const files = e.target.files;
            if (files.length === 0) return;
            const file = files[0];

            // Slice
            Rust.the_compiled_wasm_binding.then(module => 
                module.read_file(file);
            )
        

        document.getElementById("field").onchange = onChange;
    </script>
</body>
</html>

.slice() 函数的行为与 javascript 中的相同(整个文件未加载到 RAM 中),因此您可以在 WASM 中加载文件的块并执行搜索。

请注意,slice()stdweb 实现使用内部执行的 slice_blob()

js! (
    return @reference.slice(@start, @end, @content_type);
).try_into().unwrap()

正如你所看到的,它在底层使用了 javascript,所以这里没有优化。

结论

恕我直言,文件读取实现在javascript 中更有效,原因是:

stdweb::File API 在底层使用原始 javascript(因此速度不会更快) stdweb::File 的功能比 javascript 少(缺少流式传输和其他一些功能)。

那么确实可以/应该在 WASM 中实现搜索算法。该算法可以直接处理一个块(Blob)进行处理。

【讨论】:

感谢您。您能否详细说明这一点:IMHO this implementation is more effective in javascript?例如,您是否只是说字符串搜索的 js(例如 Chrome)c++ 实现会比将 blob 传递给 WASM 快得多,因为“传输”需要很长时间? source.chromium.org/chromium/chromium/src/+/main:third_party/… @David542(我编辑了答案)文件读取部分在 JS 中的效率将与在 WASM 中一样高效(在后台使用 JS)。所以我建议通过使用FileReaderstdweb 中不可用)流式传输 JS 中的块,然后将 Blob 传递给 WASM。如果您的搜索像 string search 一样简单,那么我认为您根本不需要 WASM(字符串搜索已经优化)。对于复杂的表扫描/二进制搜索,请使用 WASM(但仍仅用于 Blob 处理)。 你知道通过 emscripten 的 c/c++ 是否也只是在底层使用 javascript 来读取文件,因此无论是用 C++ 还是 js 读取文件都没有区别? 也许不是,见emscripten.org/docs/api_reference/Filesystem-API.html#workerfs。但是带有 emscripten 的文件更加混乱。为了做出任何假设,我需要像为 stdweb 所做的那样深入研究源代码。

以上是关于使用 webassembly 在浏览器中查询大型数据集的主要内容,如果未能解决你的问题,请参考以下文章

为什么说 WebAssembly 属于浏览器之外? Why WebAssembly Belongs Outside the Browser

为什么说 WebAssembly 属于浏览器之外? Why WebAssembly Belongs Outside the Browser

Shopify如何在浏览器之外使用WebAssembly?

尝试使用 WebAssembly Explorer

如何在浏览器中使用WebAssembly特性

《WebAssembly 权威指南》在浏览器中运行遗留代码