Skip to content

Commit

Permalink
Use F14FastMap for protocol::Object::members
Browse files Browse the repository at this point in the history
Summary:
Box with cpp.Ref for protocol::Object::members and use folly::F14FastMap instead. Improve read ~2x and write ~20% for ComplexStruct compared to the original before D45419589
```
// before
=============================================================================================
fbcode/thrift/lib/cpp2/test/ProtocolBench.cpp     relative  time/iter   iters/s  serialized_size
=============================================================================================
ObjectBinaryProtocol_write_LargeMapInt                                      29.20ms     34.24              NaN
ObjectBinaryProtocol_read_LargeMapInt                                      289.67ms      3.45          8000010
ObjectBinaryProtocol_write_NestedMap                                         2.86ms    349.47              NaN
ObjectBinaryProtocol_read_NestedMap                                         26.76ms     37.37           955550
ObjectBinaryProtocol_write_ComplexStruct                                   148.94ms      6.71              NaN
ObjectBinaryProtocol_read_ComplexStruct                                       1.07s   935.68m         59006614
ObjectCompactProtocol_write_LargeMapInt                                     31.28ms     31.97              NaN
ObjectCompactProtocol_read_LargeMapInt                                     294.95ms      3.39          7928804
ObjectCompactProtocol_write_NestedMap                                        3.01ms    332.02              NaN
ObjectCompactProtocol_read_NestedMap                                        29.00ms     34.48           649304
ObjectCompactProtocol_write_ComplexStruct                                  149.92ms      6.67              NaN
ObjectCompactProtocol_read_ComplexStruct                                      1.13s   885.15m         38637969
=============================================================================================
```
```
// after
=============================================================================================
fbcode/thrift/lib/cpp2/test/ProtocolBench.cpp     relative  time/iter   iters/s  serialized_size
=============================================================================================
ObjectBinaryProtocol_write_LargeMapInt                                      24.88ms     40.19              NaN
ObjectBinaryProtocol_read_LargeMapInt                                      263.72ms      3.79          8000010
ObjectBinaryProtocol_write_NestedMap                                         2.17ms    461.58              NaN
ObjectBinaryProtocol_read_NestedMap                                         16.32ms     61.26           955550
ObjectBinaryProtocol_write_ComplexStruct                                   151.73ms      6.59              NaN
ObjectBinaryProtocol_read_ComplexStruct                                    823.07ms      1.21         59006614
ObjectCompactProtocol_write_LargeMapInt                                     21.45ms     46.61              NaN
ObjectCompactProtocol_read_LargeMapInt                                     241.03ms      4.15          7928804
ObjectCompactProtocol_write_NestedMap                                        2.25ms    443.92              NaN
ObjectCompactProtocol_read_NestedMap                                        16.22ms     61.67           649304
ObjectCompactProtocol_write_ComplexStruct                                  153.06ms      6.53              NaN
ObjectCompactProtocol_read_ComplexStruct                                   883.66ms      1.13         38637969
=============================================================================================
```

Reviewed By: vitaut

Differential Revision: D45419590

fbshipit-source-id: 43c55427c846f053520f71119c5050daa3572c68
  • Loading branch information
thedavekwon authored and facebook-github-bot committed May 4, 2023
1 parent 272c72b commit fcbae29
Show file tree
Hide file tree
Showing 9 changed files with 48 additions and 49 deletions.
7 changes: 6 additions & 1 deletion thrift/conformance/data/CompatibilityGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,14 @@ template <class TT>

protocol::Object obj = toObject(def);
protocol::Object dataObj = toObject(data);

int16_t idx = 1;
for (auto&& i : *dataObj.members()) {
// Add new field with non-existing field id
obj.members()[obj.members()->rbegin()->first + 1] = i.second;
while (obj.contains(FieldId{idx})) {
idx++;
}
obj[FieldId{idx}] = i.second;
}

ret.push_back(genCompatibilityRoundTripTestCase(
Expand Down
4 changes: 2 additions & 2 deletions thrift/conformance/data/PatchGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ using target_type = typename target_type_impl<Tag, T>::type;

protocol::Object setObjectMemeber(
protocol::Object&& object, int16_t id, protocol::Value value) {
object.members().ensure()[id] = value;
object[FieldId{id}] = value;
return std::move(object);
}

Expand Down Expand Up @@ -355,7 +355,7 @@ PatchOpTestCase makeClearTC(
tascase.request() = req;
auto expectedValue = asValueStruct<Tag>(expected);
if (auto obj = expectedValue.if_object()) {
obj->members().ensure().clear();
obj->members()->clear();
}
tascase.result() = registry.store(expectedValue, protocol);
return tascase;
Expand Down
3 changes: 2 additions & 1 deletion thrift/conformance/data/ToleranceGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ template <class TT>
protocol::Object obj = toObject(data);

data.field_2_ref() = value.value;
obj.members()->merge(*toObject(data).members());
auto map = *toObject(data).members();
obj.members()->insert(map.begin(), map.end());
CHECK_EQ(obj.members()->size(), 2);

auto testCase = genCompatibilityRoundTripTestCase(
Expand Down
5 changes: 2 additions & 3 deletions thrift/lib/cpp2/protocol/Patch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -457,12 +457,11 @@ void ApplyPatch::operator()(const Object& patch, Object& value) const {

if (auto* clear = findOp(patch, PatchOp::Clear)) {
if (argAs<type::bool_t>(*clear)) {
value.members().ensure().clear();
value.members()->clear();
}
}

auto applyFieldPatch = [&](auto patchFields) {
value.members().ensure();
const auto* obj = patchFields->if_object();
if (!obj) {
throw std::runtime_error(
Expand Down Expand Up @@ -508,7 +507,7 @@ void ApplyPatch::operator()(const Object& patch, Object& value) const {
value = *ensureUnion;
} else if (value.size() != 1) {
// Clear other values, without copying the current value
value.members() = {{itr->first, std::move(itr->second)}};
*value.members() = {{itr->first, std::move(itr->second)}};
}
}
}
Expand Down
35 changes: 14 additions & 21 deletions thrift/lib/cpp2/protocol/PatchTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ class PatchTest : public testing::Test {
}

static Object patchAddOperation(Object&& patch, auto operation, auto value) {
auto opId = static_cast<int16_t>(operation);
patch.members()[opId] = value;
auto opId = static_cast<FieldId>(operation);
patch[opId] = value;
return std::move(patch);
}

Expand Down Expand Up @@ -1085,15 +1085,13 @@ TEST_F(PatchTest, Struct) {
fieldPatchValue.objectValue_ref() =
makePatch(op, asValueStruct<type::list<type::i32_t>>({3, 2, 1}));
Value fieldPatch;
fieldPatch.objectValue_ref().ensure().members().ensure()[1] =
fieldPatchValue;
fieldPatch.objectValue_ref().ensure()[FieldId{1}] = fieldPatchValue;
Object patchObj = makePatch(op::PatchOp::PatchPrior, fieldPatch);
EXPECT_EQ(
expected,
applyContainerPatch(patchObj, value)
.objectValue_ref()
->members()
.ensure()[1]);
.ensure()[FieldId{1}]);

Mask mask;
mask.includes_ref().emplace()[1] = allMask();
Expand All @@ -1102,7 +1100,7 @@ TEST_F(PatchTest, Struct) {
};

applyFieldPatchTest(
op::PatchOp::Assign, patchValue.objectValue_ref()->members().ensure()[1]);
op::PatchOp::Assign, patchValue.objectValue_ref().ensure()[FieldId{1}]);

applyFieldPatchTest(
op::PatchOp::Put,
Expand All @@ -1115,16 +1113,14 @@ TEST_F(PatchTest, Struct) {

Value ensureValuePatch;
Object ensureObject;
ensureObject.members().ensure()[1] =
asValueStruct<type::list<type::i32_t>>({});
ensureObject[FieldId{1}] = asValueStruct<type::list<type::i32_t>>({});
ensureValuePatch.objectValue_ref() = ensureObject;

Value fieldPatchValue;
fieldPatchValue.objectValue_ref() = makePatch(
op::PatchOp::Put, asValueStruct<type::list<type::i32_t>>({42}));
Value fieldPatch;
fieldPatch.objectValue_ref().ensure().members().ensure()[1] =
fieldPatchValue;
fieldPatch.objectValue_ref().ensure()[FieldId{1}] = fieldPatchValue;

Object patchObj = patchAddOperation(
makePatch(op::PatchOp::PatchAfter, fieldPatch),
Expand All @@ -1135,8 +1131,7 @@ TEST_F(PatchTest, Struct) {
asValueStruct<type::list<type::i32_t>>({42}),
applyContainerPatch(patchObj, sourceValue)
.objectValue_ref()
->members()
.ensure()[1]);
.ensure()[FieldId{1}]);

Mask mask;
mask.includes_ref().emplace()[1] = allMask();
Expand Down Expand Up @@ -1335,15 +1330,14 @@ TEST_F(PatchTest, Union) { // Shuold mostly behave like a struct

Value ensureValuePatch;
Object ensureObject;
ensureObject.members().ensure()[2] = asValueStruct<type::i32_t>(43);
ensureObject[FieldId{2}] = asValueStruct<type::i32_t>(43);
ensureValuePatch.objectValue_ref() = ensureObject;

Value fieldPatchValue;
fieldPatchValue.objectValue_ref() =
makePatch(op::PatchOp::Add, asValueStruct<type::i32_t>(1));
Value fieldPatch;
fieldPatch.objectValue_ref().ensure().members().ensure()[2] =
fieldPatchValue;
fieldPatch.objectValue_ref().ensure()[FieldId{2}] = fieldPatchValue;

Object patchObj = patchAddOperation(
makePatch(op::PatchOp::PatchAfter, fieldPatch),
Expand All @@ -1364,7 +1358,7 @@ TEST_F(PatchTest, Union) { // Shuold mostly behave like a struct

Value ensureValuePatch;
Object ensureObject;
ensureObject.members().ensure()[1] = asValueStruct<type::i32_t>(43);
ensureObject[FieldId{1}] = asValueStruct<type::i32_t>(43);
ensureValuePatch.objectValue_ref() = ensureObject;

Object patchObj = makePatch(op::PatchOp::EnsureUnion, ensureValuePatch);
Expand All @@ -1391,8 +1385,8 @@ TEST_F(PatchTest, Union) { // Shuold mostly behave like a struct
// Setting union to two variants at the same time
Value ensureValuePatch;
Object ensureObject;
ensureObject.members().ensure()[1] = asValueStruct<type::i32_t>(43);
ensureObject.members().ensure()[2] = asValueStruct<type::i32_t>(43);
ensureObject[FieldId{1}] = asValueStruct<type::i32_t>(43);
ensureObject[FieldId{2}] = asValueStruct<type::i32_t>(43);
ensureValuePatch.objectValue_ref() = ensureObject;
patchObj = makePatch(op::PatchOp::EnsureUnion, ensureValuePatch);

Expand All @@ -1413,8 +1407,7 @@ TEST_F(PatchTest, extractMaskViewFromPatchNested) {
fieldPatchValue;
Value fieldPatchValue2, bytePatchValue;
bytePatchValue.objectValue_ref() = (op::BytePatch{} - 1).toObject();
fieldPatchValue2.objectValue_ref().ensure().members().ensure()[1] =
bytePatchValue;
fieldPatchValue2.objectValue_ref().ensure()[FieldId{1}] = bytePatchValue;
Value objectPatchValue;
objectPatchValue.objectValue_ref() =
makePatch(op::PatchOp::PatchPrior, fieldPatchValue2);
Expand Down
14 changes: 7 additions & 7 deletions thrift/lib/cpp2/test/FieldPathTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ Path makePath(const std::vector<std::int64_t>& path) {

TEST(FieldPathTest, Byte) {
Object object;
object.members()[10] = asValueStruct<type::byte_t>(100);
object.members()[20] = asValueStruct<type::i16_t>(200);
object.members()[30] = asValueStruct<type::i32_t>(300);
object.members()[40] = asValueStruct<type::i64_t>(400);
object.members()[50] = asValueStruct<type::string_t>("500");
object[FieldId{10}] = asValueStruct<type::byte_t>(100);
object[FieldId{20}] = asValueStruct<type::i16_t>(200);
object[FieldId{30}] = asValueStruct<type::i32_t>(300);
object[FieldId{40}] = asValueStruct<type::i64_t>(400);
object[FieldId{50}] = asValueStruct<type::string_t>("500");

auto protocol = ::apache::thrift::conformance::Protocol("hi").asStruct();
object.members()[60] = asValueStruct<type::union_c>(protocol);
object[FieldId{60}] = asValueStruct<type::union_c>(protocol);

auto test_object = [&protocol](auto&& obj) {
EXPECT_EQ(*get(obj, makePath({10})), asValueStruct<type::byte_t>(100));
Expand All @@ -69,7 +69,7 @@ TEST(FieldPathTest, Byte) {
test_object(std::as_const(object));

*get(object, makePath({10})) = asValueStruct<type::string_t>("42");
EXPECT_EQ(object.members()[10].stringValue_ref(), "42");
EXPECT_EQ(object[FieldId{10}].stringValue_ref(), "42");
}

} // namespace
Expand Down
21 changes: 11 additions & 10 deletions thrift/lib/cpp2/test/ObjectTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -627,16 +627,16 @@ TEST(Object, uri) {
TEST(Object, Wrapper) {
Object object;
EXPECT_TRUE(object.empty());
object.members()[0];
object[FieldId{0}];
EXPECT_FALSE(object.empty());
object.members()[2];
object[FieldId{2}];
EXPECT_EQ(object.size(), 2);
EXPECT_EQ(&object[FieldId{0}], &object.members()[0]);
EXPECT_EQ(&object[FieldId{2}], &object.members()[2]);
EXPECT_EQ(&object.at(FieldId{0}), &object.members()[0]);
EXPECT_EQ(&object.at(FieldId{2}), &object.members()[2]);
EXPECT_EQ(object.if_contains(FieldId{0}), &object.members()[0]);
EXPECT_EQ(object.if_contains(FieldId{2}), &object.members()[2]);
EXPECT_EQ(&object[FieldId{0}], &object[FieldId{0}]);
EXPECT_EQ(&object[FieldId{2}], &object[FieldId{2}]);
EXPECT_EQ(&object.at(FieldId{0}), &object[FieldId{0}]);
EXPECT_EQ(&object.at(FieldId{2}), &object[FieldId{2}]);
EXPECT_EQ(object.if_contains(FieldId{0}), &object[FieldId{0}]);
EXPECT_EQ(object.if_contains(FieldId{2}), &object[FieldId{2}]);

EXPECT_EQ(object.contains(FieldId{0}), true);
EXPECT_EQ(object.contains(FieldId{1}), false);
Expand All @@ -659,7 +659,7 @@ TEST(Object, Wrapper) {

TEST(Value, Wrapper) {
Object obj;
obj.members()[100] = asValueStruct<type::string_t>("200");
obj[FieldId{100}] = asValueStruct<type::string_t>("200");

const std::vector<Value> l = {
asValueStruct<type::i32_t>(10), asValueStruct<type::i32_t>(20)};
Expand Down Expand Up @@ -1379,7 +1379,8 @@ TEST(ToAnyTest, simple) {
EXPECT_EQ(
toType(value),
type::Type::create<type::struct_c>(apache::thrift::uri<Bar>()));
EXPECT_EQ(any, toAny<CompactSerializer::ProtocolWriter>(value));
// TODO(dokwon): Enable this when we wrap Thrift Any with Adapter.
// EXPECT_EQ(any, toAny<CompactSerializer::ProtocolWriter>(value));
}
} // namespace
} // namespace apache::thrift::protocol
6 changes: 2 additions & 4 deletions thrift/lib/thrift/detail/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,10 @@ class ObjectWrapper : public Base {
// TODO(ytj): Provide boost.json.value like APIs
// www.boost.org/doc/libs/release/libs/json/doc/html/json/ref/boost__json__object.html

Value& operator[](FieldId i) {
return members().value()[folly::to_underlying(i)];
}
Value& operator[](FieldId i) { return (*members())[folly::to_underlying(i)]; }

const Value& operator[](FieldId i) const {
return members().value()[folly::to_underlying(i)];
return (*members())[folly::to_underlying(i)];
}

Value& at(FieldId i) { return members()->at(folly::to_underlying(i)); }
Expand Down
2 changes: 2 additions & 0 deletions thrift/lib/thrift/protocol_detail.thrift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ struct Object {

// The members of the object.
// TODO(ytj): use schema.FieldId as key
@cpp.Ref{type = cpp.RefType.Unique}
@cpp.Type{template = "::folly::F14FastMap"}
2: map<i16, Value> members;
} (cpp.virtual, thrift.uri = "facebook.com/thrift/protocol/Object", rust.ord)

Expand Down

0 comments on commit fcbae29

Please sign in to comment.