cpp_padding_optimizer.cc 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. // Protocol Buffers - Google's data interchange format
  2. // Copyright 2008 Google Inc. All rights reserved.
  3. // https://developers.google.com/protocol-buffers/
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are
  7. // met:
  8. //
  9. // * Redistributions of source code must retain the above copyright
  10. // notice, this list of conditions and the following disclaimer.
  11. // * Redistributions in binary form must reproduce the above
  12. // copyright notice, this list of conditions and the following disclaimer
  13. // in the documentation and/or other materials provided with the
  14. // distribution.
  15. // * Neither the name of Google Inc. nor the names of its
  16. // contributors may be used to endorse or promote products derived from
  17. // this software without specific prior written permission.
  18. //
  19. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  22. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  23. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  24. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  25. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  26. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  27. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. #include <google/protobuf/compiler/cpp/cpp_padding_optimizer.h>
  31. #include <google/protobuf/compiler/cpp/cpp_helpers.h>
  32. namespace google {
  33. namespace protobuf {
  34. namespace compiler {
  35. namespace cpp {
  36. namespace {
  37. // FieldGroup is just a helper for PaddingOptimizer below. It holds a vector of
  38. // fields that are grouped together because they have compatible alignment, and
  39. // a preferred location in the final field ordering.
  40. class FieldGroup {
  41. public:
  42. FieldGroup() : preferred_location_(0) {}
  43. // A group with a single field.
  44. FieldGroup(float preferred_location, const FieldDescriptor* field)
  45. : preferred_location_(preferred_location), fields_(1, field) {}
  46. // Append the fields in 'other' to this group.
  47. void Append(const FieldGroup& other) {
  48. if (other.fields_.empty()) {
  49. return;
  50. }
  51. // Preferred location is the average among all the fields, so we weight by
  52. // the number of fields on each FieldGroup object.
  53. preferred_location_ = (preferred_location_ * fields_.size() +
  54. (other.preferred_location_ * other.fields_.size())) /
  55. (fields_.size() + other.fields_.size());
  56. fields_.insert(fields_.end(), other.fields_.begin(), other.fields_.end());
  57. }
  58. void SetPreferredLocation(float location) { preferred_location_ = location; }
  59. const std::vector<const FieldDescriptor*>& fields() const { return fields_; }
  60. // FieldGroup objects sort by their preferred location.
  61. bool operator<(const FieldGroup& other) const {
  62. return preferred_location_ < other.preferred_location_;
  63. }
  64. private:
  65. // "preferred_location_" is an estimate of where this group should go in the
  66. // final list of fields. We compute this by taking the average index of each
  67. // field in this group in the original ordering of fields. This is very
  68. // approximate, but should put this group close to where its member fields
  69. // originally went.
  70. float preferred_location_;
  71. std::vector<const FieldDescriptor*> fields_;
  72. // We rely on the default copy constructor and operator= so this type can be
  73. // used in a vector.
  74. };
  75. } // namespace
  76. // Reorder 'fields' so that if the fields are output into a c++ class in the new
  77. // order, fields of similar family (see below) are together and within each
  78. // family, alignment padding is minimized.
  79. //
  80. // We try to do this while keeping each field as close as possible to its field
  81. // number order so that we don't reduce cache locality much for function that
  82. // access each field in order. Originally, OptimizePadding used declaration
  83. // order for its decisions, but generated code minus the serializer/parsers uses
  84. // the output of OptimizePadding as well (stored in
  85. // MessageGenerator::optimized_order_). Since the serializers use field number
  86. // order, we use that as a tie-breaker.
  87. //
  88. // We classify each field into a particular "family" of fields, that we perform
  89. // the same operation on in our generated functions.
  90. //
  91. // REPEATED is placed first, as the C++ compiler automatically initializes
  92. // these fields in layout order.
  93. //
  94. // STRING is grouped next, as our Clear/SharedCtor/SharedDtor walks it and
  95. // calls ArenaStringPtr::Destroy on each.
  96. //
  97. //
  98. // MESSAGE is grouped next, as our Clear/SharedDtor code walks it and calls
  99. // delete on each. We initialize these fields with a NULL pointer (see
  100. // MessageFieldGenerator::GenerateConstructorCode), which allows them to be
  101. // memset.
  102. //
  103. // ZERO_INITIALIZABLE is memset in Clear/SharedCtor
  104. //
  105. // OTHER these fields are initialized one-by-one.
  106. void PaddingOptimizer::OptimizeLayout(
  107. std::vector<const FieldDescriptor*>* fields, const Options& options) {
  108. // The sorted numeric order of Family determines the declaration order in the
  109. // memory layout.
  110. enum Family {
  111. REPEATED = 0,
  112. STRING = 1,
  113. MESSAGE = 3,
  114. ZERO_INITIALIZABLE = 4,
  115. OTHER = 5,
  116. kMaxFamily
  117. };
  118. // First divide fields into those that align to 1 byte, 4 bytes or 8 bytes.
  119. std::vector<FieldGroup> aligned_to_1[kMaxFamily];
  120. std::vector<FieldGroup> aligned_to_4[kMaxFamily];
  121. std::vector<FieldGroup> aligned_to_8[kMaxFamily];
  122. for (int i = 0; i < fields->size(); ++i) {
  123. const FieldDescriptor* field = (*fields)[i];
  124. Family f = OTHER;
  125. if (field->is_repeated()) {
  126. f = REPEATED;
  127. } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
  128. f = STRING;
  129. } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
  130. f = MESSAGE;
  131. } else if (CanInitializeByZeroing(field)) {
  132. f = ZERO_INITIALIZABLE;
  133. }
  134. const int j = field->number();
  135. switch (EstimateAlignmentSize(field)) {
  136. case 1:
  137. aligned_to_1[f].push_back(FieldGroup(j, field));
  138. break;
  139. case 4:
  140. aligned_to_4[f].push_back(FieldGroup(j, field));
  141. break;
  142. case 8:
  143. aligned_to_8[f].push_back(FieldGroup(j, field));
  144. break;
  145. default:
  146. GOOGLE_LOG(FATAL) << "Unknown alignment size " << EstimateAlignmentSize(field)
  147. << "for a field " << field->full_name() << ".";
  148. }
  149. }
  150. // For each family, group fields to optimize padding.
  151. for (int f = 0; f < kMaxFamily; f++) {
  152. // Now group fields aligned to 1 byte into sets of 4, and treat those like a
  153. // single field aligned to 4 bytes.
  154. for (int i = 0; i < aligned_to_1[f].size(); i += 4) {
  155. FieldGroup field_group;
  156. for (int j = i; j < aligned_to_1[f].size() && j < i + 4; ++j) {
  157. field_group.Append(aligned_to_1[f][j]);
  158. }
  159. aligned_to_4[f].push_back(field_group);
  160. }
  161. // Sort by preferred location to keep fields as close to their field number
  162. // order as possible. Using stable_sort ensures that the output is
  163. // consistent across runs.
  164. std::stable_sort(aligned_to_4[f].begin(), aligned_to_4[f].end());
  165. // Now group fields aligned to 4 bytes (or the 4-field groups created above)
  166. // into pairs, and treat those like a single field aligned to 8 bytes.
  167. for (int i = 0; i < aligned_to_4[f].size(); i += 2) {
  168. FieldGroup field_group;
  169. for (int j = i; j < aligned_to_4[f].size() && j < i + 2; ++j) {
  170. field_group.Append(aligned_to_4[f][j]);
  171. }
  172. if (i == aligned_to_4[f].size() - 1) {
  173. if (f == OTHER) {
  174. // Move incomplete 4-byte block to the beginning. This is done to
  175. // pair with the (possible) leftover blocks from the
  176. // ZERO_INITIALIZABLE family.
  177. field_group.SetPreferredLocation(-1);
  178. } else {
  179. // Move incomplete 4-byte block to the end.
  180. field_group.SetPreferredLocation(fields->size() + 1);
  181. }
  182. }
  183. aligned_to_8[f].push_back(field_group);
  184. }
  185. // Sort by preferred location.
  186. std::stable_sort(aligned_to_8[f].begin(), aligned_to_8[f].end());
  187. }
  188. // Now pull out all the FieldDescriptors in order.
  189. fields->clear();
  190. for (int f = 0; f < kMaxFamily; ++f) {
  191. for (int i = 0; i < aligned_to_8[f].size(); ++i) {
  192. fields->insert(fields->end(), aligned_to_8[f][i].fields().begin(),
  193. aligned_to_8[f][i].fields().end());
  194. }
  195. }
  196. }
  197. } // namespace cpp
  198. } // namespace compiler
  199. } // namespace protobuf
  200. } // namespace google