使用 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 在浏览器中查询大型数据集的主要内容,如果未能解决你的问题,请参考以下文章