Reduce OatWriter RAM usage for non compilation filters
Avoid allocating full oat classes if compilation is not enabled.
This is accomplished by splitting OatClass into a new OatClassHeader
class that only has status, type, and offset members. This saves
RAM since it is much smaller than the full OatWriter::OatClass.
Maximum resident set size from /usr/bin/time -v on host dex2oat
with compiler filter verify -j4: 287248k->276716k
Bug: 63467744
Test: test-art-host
Change-Id: I6812c259cb8c8c56e122636438a76fdcc1b9a41d
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 7cb3166..7aef785 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -158,26 +158,61 @@
const void* source_;
};
+// OatClassHeader is the header only part of the oat class that is required even when compilation
+// is not enabled.
+class OatWriter::OatClassHeader {
+ public:
+ OatClassHeader(uint32_t offset,
+ uint32_t num_non_null_compiled_methods,
+ uint32_t num_methods,
+ mirror::Class::Status status)
+ : status_(status),
+ offset_(offset) {
+ // We just arbitrarily say that 0 methods means kOatClassNoneCompiled and that we won't use
+ // kOatClassAllCompiled unless there is at least one compiled method. This means in an
+ // interpreter only system, we can assert that all classes are kOatClassNoneCompiled.
+ if (num_non_null_compiled_methods == 0) {
+ type_ = kOatClassNoneCompiled;
+ } else if (num_non_null_compiled_methods == num_methods) {
+ type_ = kOatClassAllCompiled;
+ } else {
+ type_ = kOatClassSomeCompiled;
+ }
+ }
+
+ bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const;
+
+ static size_t SizeOf() {
+ return sizeof(status_) + sizeof(type_);
+ }
+
+ // Data to write.
+ static_assert(mirror::Class::Status::kStatusMax < (1 << 16), "class status won't fit in 16bits");
+ int16_t status_;
+
+ static_assert(OatClassType::kOatClassMax < (1 << 16), "oat_class type won't fit in 16bits");
+ uint16_t type_;
+
+ // Offset of start of OatClass from beginning of OatHeader. It is
+ // used to validate file position when writing.
+ uint32_t offset_;
+};
+
+// The actual oat class body contains the information about compiled methods. It is only required
+// for compiler filters that have any compilation.
class OatWriter::OatClass {
public:
- OatClass(size_t offset,
- const dchecked_vector<CompiledMethod*>& compiled_methods,
+ OatClass(const dchecked_vector<CompiledMethod*>& compiled_methods,
uint32_t num_non_null_compiled_methods,
- mirror::Class::Status status);
+ uint16_t oat_class_type);
OatClass(OatClass&& src) = default;
- size_t GetOatMethodOffsetsOffsetFromOatHeader(size_t class_def_method_index_) const;
- size_t GetOatMethodOffsetsOffsetFromOatClass(size_t class_def_method_index_) const;
size_t SizeOf() const;
- bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const;
+ bool Write(OatWriter* oat_writer, OutputStream* out) const;
CompiledMethod* GetCompiledMethod(size_t class_def_method_index) const {
return compiled_methods_[class_def_method_index];
}
- // Offset of start of OatClass from beginning of OatHeader. It is
- // used to validate file position when writing.
- size_t offset_;
-
// CompiledMethods for each class_def_method_index, or null if no method is available.
dchecked_vector<CompiledMethod*> compiled_methods_;
@@ -188,13 +223,6 @@
dchecked_vector<uint32_t> oat_method_offsets_offsets_from_oat_class_;
// Data to write.
-
- static_assert(mirror::Class::Status::kStatusMax < (1 << 16), "class status won't fit in 16bits");
- int16_t status_;
-
- static_assert(OatClassType::kOatClassMax < (1 << 16), "oat_class type won't fit in 16bits");
- uint16_t type_;
-
uint32_t method_bitmap_size_;
// bit vector indexed by ClassDef method index. When
@@ -482,6 +510,11 @@
return locations;
}
+bool OatWriter::MayHaveCompiledMethods() const {
+ return CompilerFilter::IsAnyCompilationEnabled(
+ GetCompilerDriver()->GetCompilerOptions().GetCompilerFilter());
+}
+
bool OatWriter::WriteAndOpenDexFiles(
File* vdex_file,
OutputStream* oat_rodata,
@@ -663,7 +696,10 @@
bool StartClass(const DexFile* dex_file, size_t class_def_index) OVERRIDE {
DexMethodVisitor::StartClass(dex_file, class_def_index);
- DCHECK_LT(oat_class_index_, writer_->oat_classes_.size());
+ if (kIsDebugBuild && writer_->MayHaveCompiledMethods()) {
+ // There are no oat classes if there aren't any compiled methods.
+ CHECK_LT(oat_class_index_, writer_->oat_classes_.size());
+ }
method_offsets_index_ = 0u;
return true;
}
@@ -726,7 +762,11 @@
for (const OatDexFile& oat_dex_file : writer_->oat_dex_files_) {
num_classes += oat_dex_file.class_offsets_.size();
}
- writer_->oat_classes_.reserve(num_classes);
+ // If we aren't compiling only reserve headers.
+ writer_->oat_class_headers_.reserve(num_classes);
+ if (writer->MayHaveCompiledMethods()) {
+ writer->oat_classes_.reserve(num_classes);
+ }
compiled_methods_.reserve(256u);
// If there are any classes, the class offsets allocation aligns the offset.
DCHECK(num_classes == 0u || IsAligned<4u>(offset));
@@ -770,11 +810,19 @@
}
}
- writer_->oat_classes_.emplace_back(offset_,
- compiled_methods_,
- num_non_null_compiled_methods_,
- status);
- offset_ += writer_->oat_classes_.back().SizeOf();
+ writer_->oat_class_headers_.emplace_back(offset_,
+ num_non_null_compiled_methods_,
+ compiled_methods_.size(),
+ status);
+ OatClassHeader& header = writer_->oat_class_headers_.back();
+ offset_ += header.SizeOf();
+ if (writer_->MayHaveCompiledMethods()) {
+ writer_->oat_classes_.emplace_back(compiled_methods_,
+ num_non_null_compiled_methods_,
+ header.type_);
+ offset_ += writer_->oat_classes_.back().SizeOf();
+ }
+
return DexMethodVisitor::EndClass();
}
@@ -1671,7 +1719,7 @@
if (UNLIKELY(!visitor->StartClass(dex_file, class_def_index))) {
return false;
}
- if (compiler_driver_->GetCompilerOptions().IsAnyCompilationEnabled()) {
+ if (MayHaveCompiledMethods()) {
const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
const uint8_t* class_data = dex_file->GetClassData(class_def);
if (class_data != nullptr) { // ie not an empty class, such as a marker interface
@@ -1739,21 +1787,21 @@
offset = visitor.GetOffset();
// Update oat_dex_files_.
- auto oat_class_it = oat_classes_.begin();
+ auto oat_class_it = oat_class_headers_.begin();
for (OatDexFile& oat_dex_file : oat_dex_files_) {
for (uint32_t& class_offset : oat_dex_file.class_offsets_) {
- DCHECK(oat_class_it != oat_classes_.end());
+ DCHECK(oat_class_it != oat_class_headers_.end());
class_offset = oat_class_it->offset_;
++oat_class_it;
}
}
- CHECK(oat_class_it == oat_classes_.end());
+ CHECK(oat_class_it == oat_class_headers_.end());
return offset;
}
size_t OatWriter::InitOatMaps(size_t offset) {
- if (!compiler_driver_->GetCompilerOptions().IsAnyCompilationEnabled()) {
+ if (!MayHaveCompiledMethods()) {
return offset;
}
{
@@ -2291,14 +2339,24 @@
}
size_t OatWriter::WriteClasses(OutputStream* out, size_t file_offset, size_t relative_offset) {
- for (OatClass& oat_class : oat_classes_) {
+ const bool may_have_compiled = MayHaveCompiledMethods();
+ if (may_have_compiled) {
+ CHECK_EQ(oat_class_headers_.size(), oat_classes_.size());
+ }
+ for (size_t i = 0; i < oat_class_headers_.size(); ++i) {
// If there are any classes, the class offsets allocation aligns the offset.
DCHECK_ALIGNED(relative_offset, 4u);
DCHECK_OFFSET();
- if (!oat_class.Write(this, out, oat_data_offset_)) {
+ if (!oat_class_headers_[i].Write(this, out, oat_data_offset_)) {
return 0u;
}
- relative_offset += oat_class.SizeOf();
+ relative_offset += oat_class_headers_[i].SizeOf();
+ if (may_have_compiled) {
+ if (!oat_classes_[i].Write(this, out)) {
+ return 0u;
+ }
+ relative_offset += oat_classes_[i].SizeOf();
+ }
}
return relative_offset;
}
@@ -3181,37 +3239,21 @@
return true;
}
-OatWriter::OatClass::OatClass(size_t offset,
- const dchecked_vector<CompiledMethod*>& compiled_methods,
+OatWriter::OatClass::OatClass(const dchecked_vector<CompiledMethod*>& compiled_methods,
uint32_t num_non_null_compiled_methods,
- mirror::Class::Status status)
+ uint16_t oat_class_type)
: compiled_methods_(compiled_methods) {
- uint32_t num_methods = compiled_methods.size();
+ const uint32_t num_methods = compiled_methods.size();
CHECK_LE(num_non_null_compiled_methods, num_methods);
- offset_ = offset;
oat_method_offsets_offsets_from_oat_class_.resize(num_methods);
- // Since both kOatClassNoneCompiled and kOatClassAllCompiled could
- // apply when there are 0 methods, we just arbitrarily say that 0
- // methods means kOatClassNoneCompiled and that we won't use
- // kOatClassAllCompiled unless there is at least one compiled
- // method. This means in an interpretter only system, we can assert
- // that all classes are kOatClassNoneCompiled.
- if (num_non_null_compiled_methods == 0) {
- type_ = kOatClassNoneCompiled;
- } else if (num_non_null_compiled_methods == num_methods) {
- type_ = kOatClassAllCompiled;
- } else {
- type_ = kOatClassSomeCompiled;
- }
-
- status_ = status;
method_offsets_.resize(num_non_null_compiled_methods);
method_headers_.resize(num_non_null_compiled_methods);
- uint32_t oat_method_offsets_offset_from_oat_class = sizeof(type_) + sizeof(status_);
- if (type_ == kOatClassSomeCompiled) {
+ uint32_t oat_method_offsets_offset_from_oat_class = OatClassHeader::SizeOf();
+ // We only create this instance if there are at least some compiled.
+ if (oat_class_type == kOatClassSomeCompiled) {
method_bitmap_.reset(new BitVector(num_methods, false, Allocator::GetMallocAllocator()));
method_bitmap_size_ = method_bitmap_->GetSizeOf();
oat_method_offsets_offset_from_oat_class += sizeof(method_bitmap_size_);
@@ -3228,38 +3270,22 @@
} else {
oat_method_offsets_offsets_from_oat_class_[i] = oat_method_offsets_offset_from_oat_class;
oat_method_offsets_offset_from_oat_class += sizeof(OatMethodOffsets);
- if (type_ == kOatClassSomeCompiled) {
+ if (oat_class_type == kOatClassSomeCompiled) {
method_bitmap_->SetBit(i);
}
}
}
}
-size_t OatWriter::OatClass::GetOatMethodOffsetsOffsetFromOatHeader(
- size_t class_def_method_index_) const {
- uint32_t method_offset = GetOatMethodOffsetsOffsetFromOatClass(class_def_method_index_);
- if (method_offset == 0) {
- return 0;
- }
- return offset_ + method_offset;
-}
-
-size_t OatWriter::OatClass::GetOatMethodOffsetsOffsetFromOatClass(
- size_t class_def_method_index_) const {
- return oat_method_offsets_offsets_from_oat_class_[class_def_method_index_];
-}
-
size_t OatWriter::OatClass::SizeOf() const {
- return sizeof(status_)
- + sizeof(type_)
- + ((method_bitmap_size_ == 0) ? 0 : sizeof(method_bitmap_size_))
+ return ((method_bitmap_size_ == 0) ? 0 : sizeof(method_bitmap_size_))
+ method_bitmap_size_
+ (sizeof(method_offsets_[0]) * method_offsets_.size());
}
-bool OatWriter::OatClass::Write(OatWriter* oat_writer,
- OutputStream* out,
- const size_t file_offset) const {
+bool OatWriter::OatClassHeader::Write(OatWriter* oat_writer,
+ OutputStream* out,
+ const size_t file_offset) const {
DCHECK_OFFSET_();
if (!out->WriteFully(&status_, sizeof(status_))) {
PLOG(ERROR) << "Failed to write class status to " << out->GetLocation();
@@ -3272,9 +3298,11 @@
return false;
}
oat_writer->size_oat_class_type_ += sizeof(type_);
+ return true;
+}
+bool OatWriter::OatClass::Write(OatWriter* oat_writer, OutputStream* out) const {
if (method_bitmap_size_ != 0) {
- CHECK_EQ(kOatClassSomeCompiled, type_);
if (!out->WriteFully(&method_bitmap_size_, sizeof(method_bitmap_size_))) {
PLOG(ERROR) << "Failed to write method bitmap size to " << out->GetLocation();
return false;
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 024a3e8..7b3c31c 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -239,12 +239,13 @@
return ArrayRef<const debug::MethodDebugInfo>(method_info_);
}
- const CompilerDriver* GetCompilerDriver() {
+ const CompilerDriver* GetCompilerDriver() const {
return compiler_driver_;
}
private:
class DexFileSource;
+ class OatClassHeader;
class OatClass;
class OatDexFile;
@@ -327,6 +328,8 @@
void SetMultiOatRelativePatcherAdjustment();
void CloseSources();
+ bool MayHaveCompiledMethods() const;
+
enum class WriteState {
kAddingDexFileSources,
kPrepareLayout,
@@ -410,6 +413,7 @@
// data to write
std::unique_ptr<OatHeader> oat_header_;
dchecked_vector<OatDexFile> oat_dex_files_;
+ dchecked_vector<OatClassHeader> oat_class_headers_;
dchecked_vector<OatClass> oat_classes_;
std::unique_ptr<const std::vector<uint8_t>> jni_dlsym_lookup_;
std::unique_ptr<const std::vector<uint8_t>> quick_generic_jni_trampoline_;