Skip to content

Commit 8181e14

Browse files
authored
perf(c++): directly error set instead of result to reduce cost on hotpath (#2959)
## What does this PR do? directly error set instead of result to reduce cost on hotpath for buffer read, so we can remove `Result` cost. ## Related issues #2958 ## Does this PR introduce any user-facing change? <!-- If any user-facing interface changes, please [open an issue](https://github.com/apache/fory/issues/new/choose) describing the need to do so and update the document if necessary. Delete section if not applicable. --> - [ ] Does this PR introduce any public API change? - [ ] Does this PR introduce any binary protocol compatibility change? ## Benchmark Before this PR: | Datatype | Operation | Fory (ns) | Protobuf (ns) | Faster | |----------|-----------|-----------|---------------|--------| | Struct | Serialize | 10.7 | 19.8 | Fory (1.9x) | | Struct | Deserialize | 53.7 | 16.2 | Protobuf (3.3x) | With this PR: | Datatype | Operation | Fory (ns) | Protobuf (ns) | Faster | |----------|-----------|-----------|---------------|--------| | Struct | Serialize | 10.3 | 19.8 | Fory (1.9x) | | Struct | Deserialize | 33.6 | 16.5 | Protobuf (2.0x) |
1 parent a0f3fe4 commit 8181e14

20 files changed

+1238
-393
lines changed

cpp/fory/meta/meta_string.cc

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -238,9 +238,13 @@ MetaStringTable::MetaStringTable() = default;
238238

239239
Result<std::string, Error>
240240
MetaStringTable::read_string(Buffer &buffer, const MetaStringDecoder &decoder) {
241+
Error error;
241242
// Header is encoded with VarUint32Small7 on Java side, but wire
242243
// format is still standard varuint32.
243-
FORY_TRY(header, buffer.ReadVarUint32());
244+
uint32_t header = buffer.ReadVarUint32(&error);
245+
if (FORY_PREDICT_FALSE(!error.ok())) {
246+
return Unexpected(std::move(error));
247+
}
244248
uint32_t len_or_id = header >> 1;
245249
bool is_ref = (header & 0x1u) != 0;
246250

@@ -264,16 +268,25 @@ MetaStringTable::read_string(Buffer &buffer, const MetaStringDecoder &decoder) {
264268
// The original encoding is not transmitted explicitly. For cross-language
265269
// purposes we treat the payload bytes as UTF8 and let callers handle any
266270
// higher-level semantics.
267-
FORY_TRY(hash_code, buffer.ReadInt64());
271+
int64_t hash_code = buffer.ReadInt64(&error);
272+
if (FORY_PREDICT_FALSE(!error.ok())) {
273+
return Unexpected(std::move(error));
274+
}
268275
(void)hash_code; // hash_code is only used for Java-side caching.
269276
bytes.resize(len);
270277
if (len > 0) {
271-
FORY_RETURN_NOT_OK(buffer.ReadBytes(bytes.data(), len));
278+
buffer.ReadBytes(bytes.data(), len, &error);
279+
if (FORY_PREDICT_FALSE(!error.ok())) {
280+
return Unexpected(std::move(error));
281+
}
272282
}
273283
encoding = MetaEncoding::UTF8;
274284
} else {
275285
// Small string layout: encoding(byte) + data[len]
276-
FORY_TRY(enc_byte_res, buffer.ReadInt8());
286+
int8_t enc_byte_res = buffer.ReadInt8(&error);
287+
if (FORY_PREDICT_FALSE(!error.ok())) {
288+
return Unexpected(std::move(error));
289+
}
277290
uint8_t enc_byte = static_cast<uint8_t>(enc_byte_res);
278291
if (len == 0) {
279292
if (enc_byte != 0) {
@@ -285,7 +298,10 @@ MetaStringTable::read_string(Buffer &buffer, const MetaStringDecoder &decoder) {
285298
FORY_TRY(enc, ToMetaEncoding(enc_byte));
286299
encoding = enc;
287300
bytes.resize(len);
288-
FORY_RETURN_NOT_OK(buffer.ReadBytes(bytes.data(), len));
301+
buffer.ReadBytes(bytes.data(), len, &error);
302+
if (FORY_PREDICT_FALSE(!error.ok())) {
303+
return Unexpected(std::move(error));
304+
}
289305
}
290306
}
291307

cpp/fory/row/schema.cc

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -91,23 +91,32 @@ Result<void, Error> WriteField(Buffer &buffer, const Field &field) {
9191
}
9292

9393
Result<FieldPtr, Error> ReadField(Buffer &buffer) {
94-
FORY_TRY(header_byte, buffer.ReadUint8());
94+
Error error;
95+
uint8_t header_byte = buffer.ReadUint8(&error);
96+
if (FORY_PREDICT_FALSE(!error.ok())) {
97+
return Unexpected(std::move(error));
98+
}
9599
int header = header_byte;
96100
int encoding_index = header & 0x03;
97101
int name_size_minus1 = (header >> 2) & 0x0F;
98102
bool nullable = (header & 0x40) != 0;
99103

100104
size_t name_size;
101105
if (name_size_minus1 == FIELD_NAME_SIZE_THRESHOLD) {
102-
FORY_TRY(extra, buffer.ReadVarUint32());
106+
uint32_t extra = buffer.ReadVarUint32(&error);
107+
if (FORY_PREDICT_FALSE(!error.ok())) {
108+
return Unexpected(std::move(error));
109+
}
103110
name_size = extra + FIELD_NAME_SIZE_THRESHOLD;
104111
} else {
105112
name_size = name_size_minus1 + 1;
106113
}
107114

108115
std::vector<uint8_t> name_bytes(name_size);
109-
FORY_RETURN_NOT_OK(
110-
buffer.ReadBytes(name_bytes.data(), static_cast<uint32_t>(name_size)));
116+
buffer.ReadBytes(name_bytes.data(), static_cast<uint32_t>(name_size), &error);
117+
if (FORY_PREDICT_FALSE(!error.ok())) {
118+
return Unexpected(std::move(error));
119+
}
111120

112121
MetaEncoding encoding = FIELD_NAME_ENCODINGS[encoding_index];
113122
auto decode_result =
@@ -143,7 +152,11 @@ Result<void, Error> WriteType(Buffer &buffer, const DataType &type) {
143152
}
144153

145154
Result<DataTypePtr, Error> ReadType(Buffer &buffer) {
146-
FORY_TRY(type_id_byte, buffer.ReadUint8());
155+
Error error;
156+
uint8_t type_id_byte = buffer.ReadUint8(&error);
157+
if (FORY_PREDICT_FALSE(!error.ok())) {
158+
return Unexpected(std::move(error));
159+
}
147160
TypeId type_id = static_cast<TypeId>(type_id_byte);
148161

149162
switch (type_id) {
@@ -174,8 +187,14 @@ Result<DataTypePtr, Error> ReadType(Buffer &buffer) {
174187
case TypeId::LOCAL_DATE:
175188
return date32();
176189
case TypeId::DECIMAL: {
177-
FORY_TRY(precision, buffer.ReadUint8());
178-
FORY_TRY(scale, buffer.ReadUint8());
190+
uint8_t precision = buffer.ReadUint8(&error);
191+
if (FORY_PREDICT_FALSE(!error.ok())) {
192+
return Unexpected(std::move(error));
193+
}
194+
uint8_t scale = buffer.ReadUint8(&error);
195+
if (FORY_PREDICT_FALSE(!error.ok())) {
196+
return Unexpected(std::move(error));
197+
}
179198
return decimal(precision, scale);
180199
}
181200
case TypeId::LIST: {
@@ -190,7 +209,10 @@ Result<DataTypePtr, Error> ReadType(Buffer &buffer) {
190209
std::make_shared<MapType>(key_field->type(), item_field->type()));
191210
}
192211
case TypeId::STRUCT: {
193-
FORY_TRY(struct_num_fields, buffer.ReadVarUint32());
212+
uint32_t struct_num_fields = buffer.ReadVarUint32(&error);
213+
if (FORY_PREDICT_FALSE(!error.ok())) {
214+
return Unexpected(std::move(error));
215+
}
194216
std::vector<FieldPtr> fields;
195217
fields.reserve(struct_num_fields);
196218
for (uint32_t i = 0; i < struct_num_fields; ++i) {
@@ -234,22 +256,21 @@ SchemaPtr Schema::FromBytes(const std::vector<uint8_t> &bytes) {
234256
}
235257

236258
SchemaPtr Schema::FromBytes(Buffer &buffer) {
237-
auto version_result = buffer.ReadUint8();
238-
if (!version_result.ok()) {
239-
throw std::runtime_error(version_result.error().message());
259+
Error error;
260+
uint8_t version = buffer.ReadUint8(&error);
261+
if (!error.ok()) {
262+
throw std::runtime_error(error.message());
240263
}
241-
uint8_t version = version_result.value();
242264
if (version != SCHEMA_VERSION) {
243265
throw std::runtime_error(
244266
"Unsupported schema version: " + std::to_string(version) +
245267
", expected: " + std::to_string(SCHEMA_VERSION));
246268
}
247269

248-
auto num_fields_result = buffer.ReadVarUint32();
249-
if (!num_fields_result.ok()) {
250-
throw std::runtime_error(num_fields_result.error().message());
270+
uint32_t num_fields = buffer.ReadVarUint32(&error);
271+
if (!error.ok()) {
272+
throw std::runtime_error(error.message());
251273
}
252-
uint32_t num_fields = num_fields_result.value();
253274

254275
std::vector<FieldPtr> fields;
255276
fields.reserve(num_fields);

cpp/fory/serialization/array_serializer.h

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,12 @@ struct Serializer<
9393
if (!has_value) {
9494
return std::array<T, N>();
9595
}
96+
Error error;
9697
if (read_type) {
97-
FORY_TRY(type_id_read, ctx.read_varuint32());
98+
uint32_t type_id_read = ctx.read_varuint32(&error);
99+
if (FORY_PREDICT_FALSE(!error.ok())) {
100+
return Unexpected(std::move(error));
101+
}
98102
if (type_id_read != static_cast<uint32_t>(type_id)) {
99103
return Unexpected(
100104
Error::type_mismatch(type_id_read, static_cast<uint32_t>(type_id)));
@@ -105,7 +109,11 @@ struct Serializer<
105109

106110
static Result<std::array<T, N>, Error> read_data(ReadContext &ctx) {
107111
// Read array length
108-
FORY_TRY(length, ctx.read_varuint32());
112+
Error error;
113+
uint32_t length = ctx.read_varuint32(&error);
114+
if (FORY_PREDICT_FALSE(!error.ok())) {
115+
return Unexpected(std::move(error));
116+
}
109117

110118
if (length != N) {
111119
return Unexpected(Error::invalid_data("Array size mismatch: expected " +
@@ -115,7 +123,10 @@ struct Serializer<
115123

116124
std::array<T, N> arr;
117125
if constexpr (N > 0) {
118-
FORY_RETURN_NOT_OK(ctx.read_bytes(arr.data(), N * sizeof(T)));
126+
ctx.read_bytes(arr.data(), N * sizeof(T), &error);
127+
if (FORY_PREDICT_FALSE(!error.ok())) {
128+
return Unexpected(std::move(error));
129+
}
119130
}
120131
return arr;
121132
}
@@ -161,8 +172,12 @@ template <size_t N> struct Serializer<std::array<bool, N>> {
161172
if (!has_value) {
162173
return std::array<bool, N>();
163174
}
175+
Error error;
164176
if (read_type) {
165-
FORY_TRY(type_id_read, ctx.read_varuint32());
177+
uint32_t type_id_read = ctx.read_varuint32(&error);
178+
if (FORY_PREDICT_FALSE(!error.ok())) {
179+
return Unexpected(std::move(error));
180+
}
166181
if (type_id_read != static_cast<uint32_t>(type_id)) {
167182
return Unexpected(
168183
Error::type_mismatch(type_id_read, static_cast<uint32_t>(type_id)));
@@ -173,15 +188,22 @@ template <size_t N> struct Serializer<std::array<bool, N>> {
173188

174189
static Result<std::array<bool, N>, Error> read_data(ReadContext &ctx) {
175190
// Read array length
176-
FORY_TRY(length, ctx.read_varuint32());
191+
Error error;
192+
uint32_t length = ctx.read_varuint32(&error);
193+
if (FORY_PREDICT_FALSE(!error.ok())) {
194+
return Unexpected(std::move(error));
195+
}
177196
if (length != N) {
178197
return Unexpected(Error::invalid_data("Array size mismatch: expected " +
179198
std::to_string(N) + " but got " +
180199
std::to_string(length)));
181200
}
182201
std::array<bool, N> arr;
183202
for (size_t i = 0; i < N; ++i) {
184-
FORY_TRY(byte, ctx.read_uint8());
203+
uint8_t byte = ctx.read_uint8(&error);
204+
if (FORY_PREDICT_FALSE(!error.ok())) {
205+
return Unexpected(std::move(error));
206+
}
185207
arr[i] = (byte != 0);
186208
}
187209
return arr;

0 commit comments

Comments
 (0)