Compare commits

..

5 Commits

Author SHA1 Message Date
charles 847c6cdc66 Merge branch 'check-conformance' 2026-05-04 23:38:50 -07:00
charles e5c2479985 Document unsupported Proto3 features 2026-05-04 23:38:11 -07:00
charles d21ff797b0 Add read-update-write benchmark
Update the README with a new benchmark group and performance results.
Include new data files and update the Rust benchmark implementation.
Regenerate UPB bindings and fix a data path in the C benchmark runner.
2026-05-04 23:37:36 -07:00
charles a0c2747583 Add read-update-write benchmarks
Add benchmarks for parsing, updating, and serializing in Rust and C.
Update the fork instructions in AGENTS.md.
2026-05-04 23:13:53 -07:00
charles 40e342f221 Document Protobuf spec validation 2026-05-04 23:12:20 -07:00
10 changed files with 590 additions and 332 deletions
+1 -1
View File
@@ -14,6 +14,6 @@ If you are writing code, write tests first. The tests must pass for your work to
If the users asks you to fork off and work on something, this means that you should:
1. Create a temporary directory using `mktemp -d`
2. Create a new branch to work on
2. Create a new branch to work on `git branch xyz`
3. Use git worktree to extract that branch to the temporary directory (i.e., `git worktree add $TMP_DIR -- $BRANCH)
4. Work from that directory (i.e., `cd $TMP_DIR`)
+41 -3
View File
@@ -148,11 +148,12 @@ Two benchmark suites share the same binary data files and the same four
measurement groups:
| Group | What is timed |
| --------------- | ------------------------------------------------------- |
| ------------------- | ------------------------------------------------------- |
| `shallow_parse` | Become ready to read any field (one scan / full decode) |
| `deep_parse` | Walk the full tree: Campaign → Operations → Hackers |
| `field_access` | Read individual fields on an already-parsed message |
| `iterate` | Count top-level and nested repeated fields |
| `read_update_write` | Parse, update a field, and serialize back to a buffer |
### 1 — Generate the shared data files (do this once)
@@ -267,6 +268,18 @@ criterion medians; C/upb times are the custom runner's mean over ≥ 0.5 s.
> `shallow_parse`. `count_all_crew` also parses each `Operation` sub-message;
> roto's per-level scans remain cheaper than upb's full decode.
#### `read_update_write` — parse, update a field, and serialize back to a buffer
| Size | Bytes | roto (ns) | upb (ns) | roto speedup |
| ------ | --------: | --------: | ----------: | -----------: |
| tiny | 588 | 153.8 | 1,120.3 | **7.3×** |
| small | 20,265 | 1,301.8 | 42,089.6 | **32.3×** |
| medium | 2,071,053 | 302,090.0 | 9,233,397.9 | **30.5×** |
> roto's `with()` method allows copying fields directly from the original binary
> without decoding them, making the update process extremely efficient. upb must
> fully parse the message into structs and then re-serialize the entire tree.
### Interpreting the comparison
The two libraries have fundamentally different models:
@@ -283,6 +296,31 @@ after parsing is faster. For workloads that read every field the costs
invert; for workloads that read a handful of fields from large messages roto
wins.
## Literature
## Protobuf Spec Validation
https://protobuf.dev/programming-guides/encoding/
The goal is to validate roto's implementation against the Proto3 specification.
### Supported Features
- **Scalar Types**: `double`, `float`, `int32`, `int64`, `uint32`, `uint64`, `sint32`, `sint64`, `fixed32`, `fixed64`, `sfixed32`, `sfixed64`, `bool`, `string`, `bytes`.
- **Messages**: Top-level and nested message definitions.
- **Enums**: Enum definitions with `from_i32` conversion.
- **Field Labels**: Singular and `repeated` fields.
### Unsupported Features
- **Default Values**: Missing fields return `RotoError::FieldNotFound` instead of the Proto3 default value (e.g., `0` or `""`).
- **Field Presence**: No `has_field()` methods are generated to distinguish between a field being absent and a field being set to its default value.
- **`oneof` Fields**: Not currently supported in code generation.
- **`map` Fields**: Not currently supported.
- **Packed Repeated Fields**: `roto` expects individual tags for all repeated elements; it does not support the packed encoding used for scalar numeric types in Proto3.
- **Reserved Fields**: `reserved` statements are ignored.
- **Services**: `service` and `rpc` definitions are ignored.
- **Options**: Field and message options are ignored.
### Tasks
- [x] Analyze `roto/codegen` to determine which protobuf constructs are supported during code generation.
- [x] Analyze `roto/runtime` to determine which wire types and protobuf types are supported during reading and writing.
- [x] Compare findings with the Proto3 spec (https://protobuf.dev/reference/protobuf/proto3-spec/).
- [x] Document supported and unsupported features in the README.
+32 -2
View File
@@ -27,7 +27,7 @@
//! - `iterate` — counting repeated fields at different nesting depths
use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
use roto_benches::hackers::{Campaign, Hacker, Operation, Worm};
use roto_benches::hackers::{Campaign, CampaignBuilder, Hacker, Operation, Worm};
use std::hint::black_box;
// =============================================================================
@@ -205,11 +205,41 @@ fn bench_iterate(c: &mut Criterion) {
group.finish();
}
/// Parse, update a field, and serialize back to a buffer.
fn bench_read_update_write(c: &mut Criterion) {
let cases = [
("tiny", load("tiny")),
("small", load("small")),
("medium", load("medium")),
];
let mut group = c.benchmark_group("read_update_write");
for (label, maybe_data) in &cases {
let Some(data) = maybe_data else { continue };
group.throughput(Throughput::Bytes(data.len() as u64));
group.bench_with_input(BenchmarkId::new("Campaign", label), data, |b, data| {
b.iter(|| {
let campaign = Campaign::new(data).unwrap();
let mut buf = vec![0u8; data.len() * 2];
let res = CampaignBuilder::builder(&mut buf)
.name("updated")
.unwrap()
.with(&campaign)
.unwrap()
.finish();
black_box(res.unwrap().len())
})
});
}
group.finish();
}
criterion_group!(
benches,
bench_shallow_parse,
bench_deep_parse,
bench_field_access,
bench_iterate
bench_iterate,
bench_read_update_write
);
criterion_main!(benches);
Binary file not shown.
Binary file not shown.
Binary file not shown.
+356 -255
View File
File diff suppressed because it is too large Load Diff
+124 -58
View File
@@ -13,49 +13,73 @@
// Must be last.
#include "upb/port/def.inc"
extern const struct upb_MiniTable UPB_PRIVATE(_kUpb_MiniTable_StaticallyTreeShaken);
static const upb_MiniTableField Tool__fields[5] = {
extern const UPB_PRIVATE(upb_GeneratedExtensionListEntry)* UPB_PRIVATE(upb_generated_extension_list);
typedef struct {
upb_MiniTableField fields[5];
} Tool_msg_init_Fields;
static const Tool_msg_init_Fields Tool__fields = {{
{1, 16, 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)},
{2, UPB_SIZE(24, 32), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)},
{3, UPB_SIZE(32, 48), 0, kUpb_NoSub, 12, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)},
{4, 8, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)},
{5, 12, 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)},
};
}};
const upb_MiniTable Tool_msg_init = {
NULL,
&Tool__fields[0],
UPB_SIZE(40, 64), 5, kUpb_ExtMode_NonExtendable, 5, UPB_FASTTABLE_MASK(255), 0,
&Tool__fields.fields[0],
UPB_SIZE(40, 64), 5, kUpb_ExtMode_NonExtendable, 5, UPB_FASTTABLE_MASK(56), 0,
#ifdef UPB_TRACING_ENABLED
"Tool",
#endif
UPB_FASTTABLE_INIT({
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x001000003f00000a, &upb_DecodeFast_String_Scalar_Tag1Byte},
{0x002000003f000012, &upb_DecodeFast_String_Scalar_Tag1Byte},
{0x003000003f00001a, &upb_DecodeFast_Bytes_Scalar_Tag1Byte},
{0x000800003f000020, &upb_DecodeFast_Bool_Scalar_Tag1Byte},
{0x000c00003f000028, &upb_DecodeFast_Varint32_Scalar_Tag1Byte},
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
})
};
const upb_MiniTable* Tool_msg_init_ptr = &Tool_msg_init;
static const upb_MiniTableField Connection__fields[5] = {
typedef struct {
upb_MiniTableField fields[5];
} Connection_msg_init_Fields;
static const Connection_msg_init_Fields Connection__fields = {{
{1, 16, 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)},
{2, 12, 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)},
{3, 8, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)},
{4, UPB_SIZE(32, 48), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)},
{5, UPB_SIZE(24, 32), 0, kUpb_NoSub, 12, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)},
};
}};
const upb_MiniTable Connection_msg_init = {
NULL,
&Connection__fields[0],
UPB_SIZE(40, 56), 5, kUpb_ExtMode_NonExtendable, 5, UPB_FASTTABLE_MASK(255), 0,
&Connection__fields.fields[0],
UPB_SIZE(40, 56), 5, kUpb_ExtMode_NonExtendable, 5, UPB_FASTTABLE_MASK(56), 0,
#ifdef UPB_TRACING_ENABLED
"Connection",
#endif
UPB_FASTTABLE_INIT({
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x001000003f00000a, &upb_DecodeFast_String_Scalar_Tag1Byte},
{0x000c00003f000010, &upb_DecodeFast_Varint32_Scalar_Tag1Byte},
{0x000800003f000018, &upb_DecodeFast_Bool_Scalar_Tag1Byte},
{0x003000003f000020, &upb_DecodeFast_Varint64_Scalar_Tag1Byte},
{0x002000003f00002a, &upb_DecodeFast_Bytes_Scalar_Tag1Byte},
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
})
};
const upb_MiniTable* Connection_msg_init_ptr = &Connection_msg_init;
static const upb_MiniTableSubInternal Hacker__submsgs[2] = {
{.UPB_PRIVATE(submsg) = &Tool_msg_init_ptr},
{.UPB_PRIVATE(submsg) = &Connection_msg_init_ptr},
};
typedef struct {
upb_MiniTableField fields[9];
upb_MiniTableSubInternal subs[2];
} Hacker_msg_init_Fields;
static const upb_MiniTableField Hacker__fields[9] = {
static const Hacker_msg_init_Fields Hacker__fields = {{
{1, UPB_SIZE(32, 24), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)},
{2, 40, 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)},
{3, 12, 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)},
@@ -63,96 +87,138 @@ static const upb_MiniTableField Hacker__fields[9] = {
{5, 9, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)},
{6, UPB_SIZE(48, 56), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)},
{7, UPB_SIZE(20, 64), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)},
{8, UPB_SIZE(24, 72), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)},
{9, UPB_SIZE(28, 80), 64, 1, 11, (int)kUpb_FieldMode_Scalar | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)},
};
{8, UPB_SIZE(24, 72), 0, UPB_SIZE(6, 7), 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)},
{9, UPB_SIZE(28, 80), 64, UPB_SIZE(4, 6), 11, (int)kUpb_FieldMode_Scalar | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)},
},
{
{.UPB_PRIVATE(submsg) = &Tool_msg_init},
{.UPB_PRIVATE(submsg) = &Connection_msg_init},
}};
const upb_MiniTable Hacker_msg_init = {
&Hacker__submsgs[0],
&Hacker__fields[0],
&Hacker__fields.fields[0],
UPB_SIZE(56, 88), 9, kUpb_ExtMode_NonExtendable, 9, UPB_FASTTABLE_MASK(56), 0,
#ifdef UPB_TRACING_ENABLED
"Hacker",
#endif
UPB_FASTTABLE_INIT({
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x001800003f00000a, &upb_DecodeFast_String_Scalar_Tag1Byte},
{0x002800003f000012, &upb_DecodeFast_String_Scalar_Tag1Byte},
{0x000c00003f000018, &upb_DecodeFast_Varint32_Scalar_Tag1Byte},
{0x001000003f000025, &upb_DecodeFast_Fixed32_Scalar_Tag1Byte},
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x000900003f000028, &upb_DecodeFast_Bool_Scalar_Tag1Byte},
{0x003800003f000030, &upb_DecodeFast_Varint64_Scalar_Tag1Byte},
{0x004000003f00003a, &upb_DecodeFast_String_Repeated_Tag1Byte},
})
};
const upb_MiniTable* Hacker_msg_init_ptr = &Hacker_msg_init;
static const upb_MiniTableField Worm__fields[6] = {
typedef struct {
upb_MiniTableField fields[6];
} Worm_msg_init_Fields;
static const Worm_msg_init_Fields Worm__fields = {{
{1, UPB_SIZE(20, 16), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)},
{2, 12, 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)},
{3, UPB_SIZE(40, 48), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)},
{4, UPB_SIZE(28, 32), 0, kUpb_NoSub, 12, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)},
{5, 8, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)},
{6, UPB_SIZE(16, 56), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)},
};
}};
const upb_MiniTable Worm_msg_init = {
NULL,
&Worm__fields[0],
UPB_SIZE(48, 64), 6, kUpb_ExtMode_NonExtendable, 6, UPB_FASTTABLE_MASK(255), 0,
&Worm__fields.fields[0],
UPB_SIZE(48, 64), 6, kUpb_ExtMode_NonExtendable, 6, UPB_FASTTABLE_MASK(56), 0,
#ifdef UPB_TRACING_ENABLED
"Worm",
#endif
UPB_FASTTABLE_INIT({
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x001000003f00000a, &upb_DecodeFast_String_Scalar_Tag1Byte},
{0x000c00003f000010, &upb_DecodeFast_Varint32_Scalar_Tag1Byte},
{0x003000003f000018, &upb_DecodeFast_Varint64_Scalar_Tag1Byte},
{0x002000003f000022, &upb_DecodeFast_Bytes_Scalar_Tag1Byte},
{0x000800003f000028, &upb_DecodeFast_Bool_Scalar_Tag1Byte},
{0x003800003f000032, &upb_DecodeFast_String_Repeated_Tag1Byte},
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
})
};
const upb_MiniTable* Worm_msg_init_ptr = &Worm_msg_init;
static const upb_MiniTableSubInternal Operation__submsgs[2] = {
{.UPB_PRIVATE(submsg) = &Hacker_msg_init_ptr},
{.UPB_PRIVATE(submsg) = &Worm_msg_init_ptr},
};
typedef struct {
upb_MiniTableField fields[9];
upb_MiniTableSubInternal subs[2];
} Operation_msg_init_Fields;
static const upb_MiniTableField Operation__fields[9] = {
static const Operation_msg_init_Fields Operation__fields = {{
{1, UPB_SIZE(28, 16), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)},
{2, UPB_SIZE(36, 32), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)},
{3, UPB_SIZE(56, 64), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)},
{4, 9, 0, kUpb_NoSub, 8, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_1Byte << kUpb_FieldRep_Shift)},
{5, UPB_SIZE(44, 48), 0, kUpb_NoSub, 12, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)},
{6, UPB_SIZE(12, 72), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)},
{7, UPB_SIZE(16, 80), 64, 1, 11, (int)kUpb_FieldMode_Scalar | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)},
{6, UPB_SIZE(12, 72), 0, UPB_SIZE(12, 13), 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)},
{7, UPB_SIZE(16, 80), 64, UPB_SIZE(10, 12), 11, (int)kUpb_FieldMode_Scalar | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)},
{8, UPB_SIZE(20, 88), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)},
{9, UPB_SIZE(24, 12), 0, kUpb_NoSub, 5, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_4Byte << kUpb_FieldRep_Shift)},
};
},
{
{.UPB_PRIVATE(submsg) = &Hacker_msg_init},
{.UPB_PRIVATE(submsg) = &Worm_msg_init},
}};
const upb_MiniTable Operation_msg_init = {
&Operation__submsgs[0],
&Operation__fields[0],
UPB_SIZE(64, 96), 9, kUpb_ExtMode_NonExtendable, 9, UPB_FASTTABLE_MASK(255), 0,
&Operation__fields.fields[0],
UPB_SIZE(64, 96), 9, kUpb_ExtMode_NonExtendable, 9, UPB_FASTTABLE_MASK(120), 0,
#ifdef UPB_TRACING_ENABLED
"Operation",
#endif
UPB_FASTTABLE_INIT({
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x001000003f00000a, &upb_DecodeFast_String_Scalar_Tag1Byte},
{0x002000003f000012, &upb_DecodeFast_String_Scalar_Tag1Byte},
{0x004000003f000018, &upb_DecodeFast_Varint64_Scalar_Tag1Byte},
{0x000900003f000020, &upb_DecodeFast_Bool_Scalar_Tag1Byte},
{0x003000003f00002a, &upb_DecodeFast_Bytes_Scalar_Tag1Byte},
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x005800003f000042, &upb_DecodeFast_String_Repeated_Tag1Byte},
{0x000c00003f000048, &upb_DecodeFast_Varint32_Scalar_Tag1Byte},
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
})
};
const upb_MiniTable* Operation_msg_init_ptr = &Operation_msg_init;
static const upb_MiniTableSubInternal Campaign__submsgs[1] = {
{.UPB_PRIVATE(submsg) = &Operation_msg_init_ptr},
};
typedef struct {
upb_MiniTableField fields[3];
upb_MiniTableSubInternal subs[1];
} Campaign_msg_init_Fields;
static const upb_MiniTableField Campaign__fields[3] = {
static const Campaign_msg_init_Fields Campaign__fields = {{
{1, UPB_SIZE(12, 8), 0, kUpb_NoSub, 9, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_StringView << kUpb_FieldRep_Shift)},
{2, UPB_SIZE(8, 24), 0, 0, 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)},
{2, UPB_SIZE(8, 24), 0, UPB_SIZE(6, 7), 11, (int)kUpb_FieldMode_Array | ((int)UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte) << kUpb_FieldRep_Shift)},
{3, UPB_SIZE(24, 32), 0, kUpb_NoSub, 3, (int)kUpb_FieldMode_Scalar | ((int)kUpb_FieldRep_8Byte << kUpb_FieldRep_Shift)},
};
},
{
{.UPB_PRIVATE(submsg) = &Operation_msg_init},
}};
const upb_MiniTable Campaign_msg_init = {
&Campaign__submsgs[0],
&Campaign__fields[0],
UPB_SIZE(32, 40), 3, kUpb_ExtMode_NonExtendable, 3, UPB_FASTTABLE_MASK(255), 0,
&Campaign__fields.fields[0],
UPB_SIZE(32, 40), 3, kUpb_ExtMode_NonExtendable, 3, UPB_FASTTABLE_MASK(24), 0,
#ifdef UPB_TRACING_ENABLED
"Campaign",
#endif
UPB_FASTTABLE_INIT({
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x000800003f00000a, &upb_DecodeFast_String_Scalar_Tag1Byte},
{0x0000000000000000, &_upb_FastDecoder_DecodeGeneric},
{0x002000003f000018, &upb_DecodeFast_Varint64_Scalar_Tag1Byte},
})
};
const upb_MiniTable* Campaign_msg_init_ptr = &Campaign_msg_init;
static const upb_MiniTable *messages_layout[6] = {
&Tool_msg_init,
&Connection_msg_init,
-6
View File
@@ -19,17 +19,11 @@ extern "C" {
#endif
extern const upb_MiniTable Tool_msg_init;
extern const upb_MiniTable* Tool_msg_init_ptr;
extern const upb_MiniTable Connection_msg_init;
extern const upb_MiniTable* Connection_msg_init_ptr;
extern const upb_MiniTable Hacker_msg_init;
extern const upb_MiniTable* Hacker_msg_init_ptr;
extern const upb_MiniTable Worm_msg_init;
extern const upb_MiniTable* Worm_msg_init_ptr;
extern const upb_MiniTable Operation_msg_init;
extern const upb_MiniTable* Operation_msg_init_ptr;
extern const upb_MiniTable Campaign_msg_init;
extern const upb_MiniTable* Campaign_msg_init_ptr;
extern const upb_MiniTableFile hackers_proto_upb_file_layout;
+30 -1
View File
@@ -66,7 +66,7 @@ typedef struct {
} BenchData;
static bool load_bench_data(BenchData *out, const char *name) {
snprintf(out->path, sizeof(out->path), "../data/bench/%s.pb", name);
snprintf(out->path, sizeof(out->path), "data/bench/%s.pb", name);
FILE *f = fopen(out->path, "rb");
if (!f) {
printf("[skip] %s not found — "
@@ -342,6 +342,34 @@ static void fn_count_all_crew(void *state) {
upb_Arena_Free(arena);
}
static void fn_read_update_write(void *state) {
BenchData *d = state;
upb_Arena *arena = upb_Arena_New();
Campaign *c = Campaign_parse((const char *)d->data, d->len, arena);
upb_StringView name = { .data = "updated", .size = 7 };
Campaign_set_name(c, name);
size_t len;
char *out = Campaign_serialize(c, arena, &len);
g_sink = (uintptr_t)out;
upb_Arena_Free(arena);
}
static void bench_read_update_write(void) {
const char *sizes[] = {"tiny", "small", "medium", NULL};
printf("\n=== read_update_write ===\n");
for (int i = 0; sizes[i]; i++) {
BenchData d;
if (!load_bench_data(&d, sizes[i])) continue;
char label[80];
snprintf(label, sizeof(label), "Campaign_parse+set+serialize/%s [%zu B]", sizes[i], d.len);
run_bench(fn_read_update_write, &d, d.len, label);
free_bench_data(&d);
}
}
static void bench_iterate(void) {
const char *sizes[] = {"tiny", "small", "medium", NULL};
printf("\n=== iterate ===\n");
@@ -374,6 +402,7 @@ int main(void) {
bench_deep_parse();
bench_field_access();
bench_iterate();
bench_read_update_write();
printf("\n");
return 0;