Skip to content
File

Blob: src/workerd/tests/bench-text-encoder.c++

4.6 KB
1// Copyright (c) 2023 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4 
5#include <workerd/tests/bench-tools.h>
6#include <workerd/tests/test-fixture.h>
7 
8// Benchmark for TextEncoder.encode() and TextEncoder.encodeInto() methods.
9// Tests performance across different character types (ASCII, one-byte UTF-8, two-byte UTF-8)
10// and various string lengths (32, 256, 1024, 8192 characters) to measure UTF-8 encoding overhead.
11 
12namespace workerd {
13namespace {
14 
15struct TextEncoder: public benchmark::Fixture {
16 virtual ~TextEncoder() noexcept(true) {}
17 
18 void SetUp(benchmark::State& state) noexcept(true) override {
19 TestFixture::SetupParams params = {.mainModuleSource = R"(
20 const encoder = new TextEncoder();
21 
22 export default {
23 async fetch(request) {
24 const url = new URL(request.url);
25 const len = parseInt(url.searchParams.get('len') || '256');
26 const type = url.searchParams.get('type') || 'ascii';
27 const op = url.searchParams.get('op') || 'encode';
28 
29 let base = '';
30 switch (type) {
31 case 'ascii':
32 base = 'a';
33 break;
34 case 'one-byte':
35 base = '\xff';
36 break;
37 case 'two-byte':
38 base = 'ğ';
39 break;
40 }
41 
42 const input = base.repeat(len);
43 
44 let result;
45 if (op === 'encode') {
46 for (let i = 0; i < 1000; i++) {
47 result = encoder.encode(input);
48 }
49 return new Response(result.length.toString());
50 } else if (op === 'encodeInto') {
51 const buffer = new Uint8Array(len * 3); // enough space for any UTF-8 encoding
52 for (let i = 0; i < 1000; i++) {
53 result = encoder.encodeInto(input, buffer);
54 }
55 return new Response(result.written.toString());
56 }
57 
58 throw new Error('Invalid operation');
59 },
60 };
61 )"_kj};
62 fixture = kj::heap<TestFixture>(kj::mv(params));
63 }
64 
65 void TearDown(benchmark::State& state) noexcept(true) override {
66 fixture = nullptr;
67 }
68 
69 kj::Own<TestFixture> fixture;
70};
71 
72// Parameterized benchmark to avoid duplication
73// Args format: (operation, type, length)
74// operation: 0=encode, 1=encodeInto
75// type: 0=ascii, 1=one-byte, 2=two-byte
76BENCHMARK_DEFINE_F(TextEncoder, Parameterized)(benchmark::State& state) {
77 const char* op = state.range(0) == 0 ? "encode" : "encodeInto";
78 const char* type;
79 switch (state.range(1)) {
80 case 0:
81 type = "ascii";
82 break;
83 case 1:
84 type = "one-byte";
85 break;
86 case 2:
87 type = "two-byte";
88 break;
89 default:
90 type = "ascii";
91 break;
92 }
93 int64_t len = state.range(2);
94 
95 auto url = kj::str("http://example.com?op=", op, "&type=", type, "&len=", len);
96 
97 for (auto _: state) {
98 benchmark::DoNotOptimize(fixture->runRequest(kj::HttpMethod::GET, url, ""_kj));
99 }
100}
101 
102#define TEXT_ENCODER_BENCH(op_name, op_val, type_name, type_val, len) \
103 BENCHMARK_REGISTER_F(TextEncoder, Parameterized) \
104 ->Args({op_val, type_val, len}) \
105 ->Name(#op_name "_" #type_name "_" #len)
106 
107// Note: Google Benchmark will append the arg values to the name (e.g., "Encode_ASCII_32/0/0/32")
108// where the trailing numbers are the actual argument values passed via ->Args():
109// /0/0/32 = operation (0=encode, 1=encodeInto) / type (0=ascii, 1=one-byte, 2=two-byte) / length
110TEXT_ENCODER_BENCH(Encode, 0, ASCII, 0, 32);
111TEXT_ENCODER_BENCH(Encode, 0, ASCII, 0, 256);
112TEXT_ENCODER_BENCH(Encode, 0, ASCII, 0, 1024);
113TEXT_ENCODER_BENCH(Encode, 0, ASCII, 0, 8192);
114TEXT_ENCODER_BENCH(Encode, 0, OneByte, 1, 256);
115TEXT_ENCODER_BENCH(Encode, 0, OneByte, 1, 1024);
116TEXT_ENCODER_BENCH(Encode, 0, OneByte, 1, 8192);
117TEXT_ENCODER_BENCH(Encode, 0, TwoByte, 2, 256);
118TEXT_ENCODER_BENCH(Encode, 0, TwoByte, 2, 1024);
119TEXT_ENCODER_BENCH(Encode, 0, TwoByte, 2, 8192);
120TEXT_ENCODER_BENCH(EncodeInto, 1, ASCII, 0, 256);
121TEXT_ENCODER_BENCH(EncodeInto, 1, ASCII, 0, 1024);
122TEXT_ENCODER_BENCH(EncodeInto, 1, ASCII, 0, 8192);
123TEXT_ENCODER_BENCH(EncodeInto, 1, OneByte, 1, 256);
124TEXT_ENCODER_BENCH(EncodeInto, 1, OneByte, 1, 1024);
125TEXT_ENCODER_BENCH(EncodeInto, 1, OneByte, 1, 8192);
126TEXT_ENCODER_BENCH(EncodeInto, 1, TwoByte, 2, 256);
127TEXT_ENCODER_BENCH(EncodeInto, 1, TwoByte, 2, 1024);
128TEXT_ENCODER_BENCH(EncodeInto, 1, TwoByte, 2, 8192);
129 
130} // namespace
131} // namespace workerd