From c6f41b5b3ca3d7ac3c12ad3995ffef4e831973a0 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 4 Dec 2015 15:38:50 -0800 Subject: Add support for LZ4 compressed image files Added dex2oat option --image-format=(store|lz4). Using lz4 means that the main image section (all data other than header and bitmap) are stored in a compressed state. N5 results: Boot image size: 8067128 -> 2827605 Decompression time 18.93ms Decompression rate: 426MB/s Patchoat is not currently supported since it maps the source image directly. In order to support compressed images we would need to recompress the output image and then write it back out to a file. Also there are not many cases where we would want to patch a compressed image since they are going to be dirty memory when uncompressed anyways. Might as well just patch as we are loading. Bug: 22858531 Change-Id: I8c54ccf73408273011161a61bb891736735074d9 --- compiler/image_writer.cc | 71 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 11 deletions(-) (limited to 'compiler/image_writer.cc') diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index bf1fcdd5f5..f9f0eb8154 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -17,6 +17,7 @@ #include "image_writer.h" #include +#include #include #include @@ -225,27 +226,72 @@ bool ImageWriter::Write(int image_fd, return EXIT_FAILURE; } - // Write out the image + fields + methods. + std::unique_ptr compressed_data; + // Image data size excludes the bitmap and the header. ImageHeader* const image_header = reinterpret_cast(image_->Begin()); - const auto write_count = image_header->GetImageSize(); - if (!image_file->WriteFully(image_->Begin(), write_count)) { - PLOG(ERROR) << "Failed to write image file " << image_filename; + const size_t image_data_size = image_header->GetImageSize() - sizeof(ImageHeader); + char* image_data = reinterpret_cast(image_->Begin()) + sizeof(ImageHeader); + size_t data_size; + const char* image_data_to_write; + + CHECK_EQ(image_header->storage_mode_, image_storage_mode_); + switch (image_storage_mode_) { + case ImageHeader::kStorageModeLZ4: { + size_t compressed_max_size = LZ4_compressBound(image_data_size); + compressed_data.reset(new char[compressed_max_size]); + data_size = LZ4_compress( + reinterpret_cast(image_->Begin()) + sizeof(ImageHeader), + &compressed_data[0], + image_data_size); + image_data_to_write = &compressed_data[0]; + VLOG(compiler) << "Compressed from " << image_data_size << " to " << data_size; + break; + } + case ImageHeader::kStorageModeUncompressed: { + data_size = image_data_size; + image_data_to_write = image_data; + break; + } + default: { + LOG(FATAL) << "Unsupported"; + UNREACHABLE(); + } + } + + // Write header first, as uncompressed. + image_header->data_size_ = data_size; + if (!image_file->WriteFully(image_->Begin(), sizeof(ImageHeader))) { + PLOG(ERROR) << "Failed to write image file header " << image_filename; + image_file->Erase(); + return false; + } + + // Write out the image + fields + methods. + const bool is_compressed = compressed_data != nullptr; + if (!image_file->WriteFully(image_data_to_write, data_size)) { + PLOG(ERROR) << "Failed to write image file data " << image_filename; image_file->Erase(); return false; } - // Write out the image bitmap at the page aligned start of the image end. + // Write out the image bitmap at the page aligned start of the image end, also uncompressed for + // convenience. const ImageSection& bitmap_section = image_header->GetImageSection( ImageHeader::kSectionImageBitmap); - CHECK_ALIGNED(bitmap_section.Offset(), kPageSize); + // Align up since data size may be unaligned if the image is compressed. + size_t bitmap_position_in_file = RoundUp(sizeof(ImageHeader) + data_size, kPageSize); + if (!is_compressed) { + CHECK_EQ(bitmap_position_in_file, bitmap_section.Offset()); + } if (!image_file->Write(reinterpret_cast(image_bitmap_->Begin()), - bitmap_section.Size(), bitmap_section.Offset())) { + bitmap_section.Size(), + bitmap_position_in_file)) { PLOG(ERROR) << "Failed to write image file " << image_filename; image_file->Erase(); return false; } - - CHECK_EQ(bitmap_section.End(), static_cast(image_file->GetLength())); + CHECK_EQ(bitmap_position_in_file + bitmap_section.Size(), + static_cast(image_file->GetLength())); if (image_file->FlushCloseOrErase() != 0) { PLOG(ERROR) << "Failed to flush and close image file " << image_filename; return false; @@ -1247,7 +1293,8 @@ void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) { } CHECK_EQ(AlignUp(image_begin_ + image_end, kPageSize), oat_file_begin) << "Oat file should be right after the image."; - // Create the header. + // Create the header, leave 0 for data size since we will fill this in as we are writing the + // image. new (image_->Begin()) ImageHeader(PointerToLowMemUInt32(image_begin_), image_end, sections, @@ -1258,7 +1305,9 @@ void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) { PointerToLowMemUInt32(oat_data_end), PointerToLowMemUInt32(oat_file_end), target_ptr_size_, - compile_pic_); + compile_pic_, + image_storage_mode_, + /*data_size*/0u); } ArtMethod* ImageWriter::GetImageMethodAddress(ArtMethod* method) { -- cgit v1.2.3-59-g8ed1b