This website stores cookies on your computer. These cookies are used to collect information about how you interact with our website and allow us to remember you. We use this information in order to improve and customize your browsing experience and for analytics and metrics about our visitors both on this website and other media. To find out more about the cookies we use, see our Privacy Policy. If you decline, your information won’t be tracked when you visit this website. A single cookie will be used in your browser to remember your preference not to be tracked. Cookies settings AcceptDecline Varnish Software Talk to an Expert Menu

Talk to an Expert Return to Blog April 10, 2025 10 min read time

TinyKVM in Varnish and some Deno

Alf-André Walla Alf-André Walla

大家好。我最近写了一篇关于 TinyKVM 的文章,它是一个具有原生性能的沙箱。这次我想写一下如何将它作为 Varnish Cache 中的计算框架来尝试。我也非常渴望(至少可以这么说)我的理论是否能在实践中站得住脚。TinyKVM 真的对沙箱计算工作负载来说是最快的方法吗?每个请求的隔离如何呢? 我邀请了 Laurence Rowe 与我一起撰写关于他将基于 Rust 的 Deno JavaScript 运行时嵌入到 TinyKVM 中的冒险经历。

我之前写过 TinyKVM 运行常规 Linux ELF 文件,我在将程序加载到 Varnish 中之前,会像常规程序一样在终端上测试我的程序。当运行 嵌入在 Varnish 中的 TinyKVM 时,有一个小型 API 来方便接收 HTTP 请求并写回响应。这实际上是整个事情的关键:我专门将其设计为一个简单的“这是一个请求”和“响应在哪里?”类型的东西:

int main(int argc, char **argv) { 
    if (IS_LINUX_MAIN()) { 
        puts("Hello Linux Terminal World!"); 
        return 0; 
    } 

    while (true) { 
        struct backend_request req; 
        wait_for_requests_paused(&req); 
        http_appendf(RESP, "X-Hello: %s", "World"); 
        backend_response_str(200, "text/plain", "Hello Compute World!"); 
    } 
}

while 循环内的 wait_for_requests_paused() 将接收传入的请求。 如果程序在任何时候崩溃,它将被强制重置回暂停时的状态。 或者,您可以配置程序在每个请求后重置回暂停状态。 所谓的临时 VM——因为它们无法持久化任何东西。 实际上,它变成了每个请求的隔离。

#include "kvm_api.hpp" 
#include <simdjson/minify.h> 

static std::vector<char> buffer(16ULL << 20); // 16MB buffer 

static void on_post(const char *, const char *, const char *content_type, const uint8_t* data, const size_t len) { 
    /* Minify the JSON at 6GB/s */ 
    size_t buffer_len = buffer.size(); 
    (void)simdjson::minify((const char *)data, len, buffer.data(), buffer_len); 
    /* Respond with the minified JSON */ 
    Backend::response(200, "application/json", buffer.data(), buffer_len); 
} 

int main(int argc, char **argv) { 
    set_backend_post(on_post); 
    wait_for_requests(); 
}

上面:一个使用 simdjson 的 JSON 最小化程序。 使用静态缓冲区是为了控制非临时程序的内存使用。 当程序是临时的时,没有必要跟踪内存使用情况,因为它会在请求完成后完全重置。 换句话说,只要程序能够传递响应,就可以了,因为它会在下一个请求中以原始状态重新开始。 与非临时程序相比,依赖于 GC 的程序在临时程序中可以更快,因为可能耗时的 GC 永远不允许运行。

一个程序示例

TinyKVM Blog Image 1

我厚颜无耻地使用了我自己的 GBC 模拟器。

其中一个 TinyKVM 示例程序嵌入了一个我于 2017 年编写的 GBC 模拟器,该模拟器以网页形式呈现,并由我的朋友 Kyle 巧妙地使用了一些内联 CSS。 我曾经将它嵌入到 IncludeOS 中,添加了 PS/2 键盘支持并将其发送到 Qemu 的圣诞日历。 它被接受了! 它使用的 ROM 是一个名为 µCity Advance 由 Antonio Niño Díaz 制作的城市建造自制游戏,我得到了他的许可才能使用它!

当然,我这样做只是为了好玩。 它提供合作游戏玩法,您可以在其中争夺控制权。 祝你好运!

共享的可变存储

我想简单解释一下,尽管由独立的请求 VM 提供服务,彼此之间互不了解,但合作的 GBC 游戏玩法是如何取得进展的。该程序使用我称为存储的东西,它是程序在其中初始化的主 VM 的一个分支。 在启动期间,您设置好一切,然后在主 VM 上创建一堆轻量级分支,这些分支在下文中被称为请求 VM。 这些请求 VM 随后准备好处理请求,因为它们都是预初始化的。 剩下的是主 VM,现在可以用于其他用途:共享的可变存储。 因此,存储是程序的特殊实例。 为了使用存储,您可以(除其他事项外)调用允许的函数来传入和传出数据。 由于存储 VM 与您的请求 VM 共享程序的常量部分,因此每个静态(和静态 PIE)链接时符号都具有相同的地址。 因此,调用存储中的函数就像传递现有函数作为参数一样简单:storage_call(my_function, …) 然后将在存储中调用 my_function,只要存储允许即可。 或者您可以使用共享内存。

在示例 GBC 程序中,我们从客户端请求中使用游戏手柄输入调用存储 VM 中的函数,然后将其贡献给下一帧。 我们从存储返回的数据是当前帧的编码 PNG。我注意到的一件事是,按键往往会被保持很长时间(与单帧相比),直到它们发生变化。 所以我也通过假设相同的输入来预测下一帧。 所以:

  1. 一个请求 VM 调用一个存储函数
  2. 现在是模拟下一帧的时候了吗? 否则传递旧帧
  3. 检查是否已经预测了该帧,如果是,则返回预测
  4. 如果未预测该帧,则生成一个新帧并返回该帧
  5. 在停机期间安排一个预测帧

最后一点很重要:我们可以在存储请求之间安排一些事情发生。 存储将在请求 VM 访问之外安排的函数调用。 这减少了延迟,因为大部分工作都在编码 PNG。 您可以在此处找到源代码。当然还有更多内容。 比如能够在保持状态的同时热交换 GBC 程序并更新版本。 我们可以让 Gameboy 变绿而无需重启游戏! 但我现在必须停止,否则将永远不会结束。

具有一些 API 支持的语言

到目前为止,我已经为这些语言制作了 API,完成程度各不相同:

看看程序示例存储库。 C/C++ 拥有最完整的 API,而能够理解 C 标头的语言非常接近,例如 Zig。 Zig 确实很出色,但我确实认为编写 Zig 的人会欣赏更具 Zig 风格的 API,尽管它可以理解 C 标头。 对吧? 我征求了对我 Rust API 的看法,但它被批评得一无是处。 显然,它比我意识到的更像 C。 有人还要求提供 Python 示例,在支持加载动态链接可执行文件之前,我不会在这方面进行工作。 不过,它即将到来!我想补充一点,如果您觉得某种语言的 API 缺少某些东西,请创建一个问题并告诉我。 我的时间不是无限的,但我会尽力而为。否则,有很多事情的示例程序。 有 WebP 和 AVIF 转码器。 Zstandard 和 gzip 压缩器。 通常的事情。

Deno JavaScript 运行时

我邀请 Laurence Rowe 来撰写这一部分。 他在运行实际基准测试方面做得非常出色,并将其与现有解决方案进行比较。

现在大多数 Web UI 都是用 JavaScript 编写的,因此服务器渲染对于为初始页面加载提供最佳体验是必不可少的。 理想情况下,此类 UI 代码应该在每个请求的隔离中运行,以避免在请求之间泄漏数据的可能性——如果在错误的级别放置变量声明,这太容易了——但当前的选择速度太慢。当前每个请求隔离的方法使用 V8 隔离或进程派生,但这会产生几毫秒的额外延迟。 WebAssembly 可以为每个请求的隔离提供微秒级的延迟,但与 JIT 不兼容,因此当执行更复杂的 JS(例如使用 React 渲染页面)时,较慢的执行速度超过了较低的启动延迟。看到 TinyKVM 的性能数字确实令人兴奋,它显然在许多类型的程序中运行良好,那么它在 JavaScript 中表现如何呢? 仅 V8 就缺少轻松运行许多实际代码所需的许多 Web 平台支持。 Deno 提供了一个成熟、功能齐全的 JS 运行时,该运行时构建在 V8 之上,并且在很大程度上与 Web 平台和 Node 兼容。当前的概念验证实现通过 ~0.4 毫秒的额外延迟实现了每个请求的隔离,同时使用从实际站点获取的 React 渲染一个复杂的页面。 在我的系统上,在没有每个请求隔离且 GC 在后台线程上运行的情况下,Deno 库存的中位数渲染时间为 0.57 毫秒。 在没有每个请求隔离的情况下,在 TinyKVM 下单线程运行时的渲染时间为 0.72 毫秒,而在具有每个请求隔离的情况下为 0.95 毫秒。这似乎是目前在每个请求的隔离下运行大量 JavaScript 程序的最快选择,速度快了一个数量级! 内存占用较小的可执行文件在 TinyKVM 上具有更低的延迟,我们将继续寻找进一步的优化。当前的概念验证构建了一个静态可执行文件,该文件以单线程模式运行 deno_runtime,并提供与 Varnish TinyKVM API 足够的集成,以运行一些基准测试。向 TinyKVM 添加了运行 Rust 的 crt-static 支持,该支持在静态链接 glibc 时使用,从而避免了在 musl 下构建 Deno 的复杂性。 并且添加了一个新的 wait_for_requests_paused API,以允许 JS 同步调用 Varnish API 作为主机函数,从而避免了回调 API 需要的事件循环中的额外行程。目前尚不清楚长期来看 TinyKVM 下的 Deno 将会是什么样子。 如果 Deno 添加一个以单线程运行的选项并且 TinyKVM 能够运行动态链接的可执行文件,则有可能它会成为 Deno 扩展。 但是有很多 V8 构建选项可以探索,这可能会使自定义构建有价值。

gzip 基准测试和 Deno 中的 hugepage

我很高兴每个请求的隔离运行良好。 我现在喜欢做基准测试,而且我还针对 Varnish 中的内部 zlib 进行了快速的 gzip 基准测试:

TinyKVM Blog Image 2

libdeflate 的确像他们声称的那样快

对于这些相对较小的有效负载,zlib-ng 比嵌入在 Varnish 中的 zlib 至少提供了 33% 的性能提升。 libdeflate 声称比替代品快得多,并且确实实现了这一承诺。否则,基准测试有时可能会令人困惑:

$ ./wrk -c4 -t4 http://127.0.0.1:8080/ -H "Host: deno" 
Running 10s test @ http://127.0.0.1:8080/ 
4 threads and 4 connections 
  Thread Stats   Avg      Stdev     Max   +/- Stdev 
    Latency     103.33us   14.54us   1.16ms   78.07% 
    Req/Sec     9.68k   385.52  10.65k   69.31% 
389339 requests in 10.10s, 56.07MB read 
Requests/sec:  38549.93 
Transfer/sec:      5.55MB 

$ ./wrk -c4 -t4 http://127.0.0.1:8080/ -H "Host: deno" 
Running 10s test @ http://127.0.0.1:8080/ 
4 threads and 4 connections 
  Thread Stats   Avg      Stdev     Max   +/- Stdev 
    Latency      91.17us   11.30us 848.00us   77.06% 
    Req/Sec     10.97k   435.97  12.22k   71.29% 
440963 requests in 10.10s, 63.50MB read 
Requests/sec:  43663.15 
Transfer/sec:      6.29MB

这是启用 hugepage 和禁用 hugepage 的情况下,一个小型 Deno JS 程序的每个请求隔离的基准测试。 不是针对整个主内存,也不是针对请求 VM 中的所有工作内存。 每个页面都足以覆盖主 VM 中的只读段和请求 VM 中的热路径。 人们可以推测,除了减少页表遍历之外,hugepage 还减少了每个请求所需的 IPI 数量。 无论如何,这都是运行时设置带来的 12% 的性能提升,因为_程序没有被修改_。 我认为这是我在做所有这些事情时一直感到困惑的原因:我们只是通过启用 TinyKVM 中的设置来获得了很多性能,而无需重新编译程序。有趣的事实:Laurence 的 Deno JS UI renderer.js 有 252k 行 JavaScript!?

结论

我们可以看到 TinyKVM 提供了高性能沙箱,不仅在原始计算方面,而且在每个请求的隔离方面:

$ ./wrk -c1 -t1 http://127.0.0.1:8080/ -H "Host: test.com" 
Running 10s test @ http://127.0.0.1:8080/ 
1 threads and 1 connections 
  Thread Stats   Avg      Stdev     Max   +/- Stdev 
    Latency      14.16us    1.43us  202.00us   96.19% 
    Req/Sec     69.41k   3.26k  72.51k   90.10% 
697892 requests in 10.10s, 90.52MB read 
Requests/sec:  69100.43 
Transfer/sec:      8.96MB

在我的机器上的 HTTP 基准测试中,它可以平均在 14us 内对一个小程序进行每个请求的隔离。 也就是 14 微秒_端到端_!Varnish 中的 TinyKVM 计算框架旨在简化数据处理。 由于直接嵌入在 Varnish 中,因此它可以访问缓存并能够直接缓存数据。 我希望框架可以做的一些事情已经明确了。得知 TinyKVM 中没有任何从根本上不良扩展的东西,这减轻了我的负担。 想到因为 Deno 运行时,我制作了一种全新的重置机制,这也令人感到奇怪。 但我想说我很高兴我做到了。 新的重置机制对于某些程序来说证明要快得多! 它真的比其他每个请求隔离的解决方案快一个数量级吗!?

Laurence Rowe 和 Alf-André Walla 撰写。 博客最初发布在 这里

Related Posts

You may also like this

March 13, 2025 8 min read time

TinyKVM: The Fastest Sandbox

An introduction to a KVM-based single-process sandbox Hey All. In between working on my PhD,... February 3, 2025 10 min read time

Intel® Converged Edge Media Platform & Varnish Enterprise

About the Project This article explores how Intel®'s Converged Edge Media Platform (Reference... June 25, 2024 11 min read time

Varnish and Observability (Part 1: The Basics)

Is Varnish fast? Of course it is! I know that, you know that, but I want my friends, and my...

SUBSCRIBE TO OUR BLOG

Email* Notification Frequency

SEARCH OUR BLOG

Search Explore articles from Varnish experts on web performance, advanced caching techniques, CDN optimization and more, plus all the latest tips and insights for enhancing your content delivery operations.

Contact us

Stockholm +46 8 410 909 30 Paris +33 1 70 75 27 81 Singapore: +65 8434 8028 Contact us

Products & Services

Varnish Enterprise Varnish Controller Varnish Traffic Router Varnish Cache Support Professional Services Varnish as a Managed Service

Solutions

[Web Acceleration](https://info.varnish-software.