libsnapshot:snapuserd: Cut down memory usage
Use sorted std:vector instead of std:map to store
the mapping between chunk-id to COW operation.
Addtionally, use shrink_to_fit to cut down vector
capacity when COW operations are stored.
On a full OTA of 1.8G, Anon RSS usage is
reduced from 120MB to 68MB. No variance observed
when merge was in progress.
Bug: 182960300
Test: Full and Incremental OTA - verified memory usage
Signed-off-by: Akilesh Kailash <akailash@google.com>
Change-Id: I50cacbe0d03837a830dedcf9bd0ac9663fc68fa7
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 44a423c..7199b38 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -250,6 +250,8 @@
}
ops_ = ops_buffer;
+ ops_->shrink_to_fit();
+
return true;
}
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp
index f1fcb70..5ef9e29 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd.cpp
@@ -230,7 +230,7 @@
// Store operation pointer.
- chunk_map_[ChunkToSector(data_chunk_id)] = cow_op;
+ chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
num_ops += 1;
offset += sizeof(struct disk_exception);
cowop_riter_->Next();
@@ -422,7 +422,7 @@
de->new_chunk = data_chunk_id;
// Store operation pointer.
- chunk_map_[ChunkToSector(data_chunk_id)] = it->second;
+ chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), it->second));
offset += sizeof(struct disk_exception);
num_ops += 1;
copy_ops++;
@@ -468,6 +468,12 @@
<< "Areas : " << vec_.size();
}
+ chunk_vec_.shrink_to_fit();
+ vec_.shrink_to_fit();
+
+ // Sort the vector based on sectors as we need this during un-aligned access
+ std::sort(chunk_vec_.begin(), chunk_vec_.end(), compare);
+
SNAP_LOG(INFO) << "ReadMetadata completed. Final-chunk-id: " << data_chunk_id
<< " Num Sector: " << ChunkToSector(data_chunk_id)
<< " Replace-ops: " << replace_ops << " Zero-ops: " << zero_ops
diff --git a/fs_mgr/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/snapuserd.h
index 6ce64d8..9335364 100644
--- a/fs_mgr/libsnapshot/snapuserd.h
+++ b/fs_mgr/libsnapshot/snapuserd.h
@@ -108,7 +108,7 @@
bool ProcessIORequest();
int ReadData(sector_t sector, size_t size);
int ReadUnalignedSector(sector_t sector, size_t size,
- std::map<sector_t, const CowOperation*>::iterator& it);
+ std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it);
// Processing COW operations
bool ProcessCowOp(const CowOperation* cow_op);
@@ -164,16 +164,21 @@
bool InitializeWorkers();
std::shared_ptr<Snapuserd> GetSharedPtr() { return shared_from_this(); }
- std::map<sector_t, const CowOperation*>& GetChunkMap() { return chunk_map_; }
+ std::vector<std::pair<sector_t, const CowOperation*>>& GetChunkVec() { return chunk_vec_; }
const std::vector<std::unique_ptr<uint8_t[]>>& GetMetadataVec() const { return vec_; }
+ static bool compare(std::pair<sector_t, const CowOperation*> p1,
+ std::pair<sector_t, const CowOperation*> p2) {
+ return p1.first < p2.first;
+ }
+
private:
std::vector<std::unique_ptr<WorkerThread>> worker_threads_;
- bool ReadMetadata();
bool IsChunkIdMetadata(chunk_t chunk);
chunk_t GetNextAllocatableChunkId(chunk_t chunk_id);
+ bool ReadMetadata();
sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
bool IsBlockAligned(int read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
@@ -197,15 +202,9 @@
// mapping of old-chunk to new-chunk
std::vector<std::unique_ptr<uint8_t[]>> vec_;
- // Key - Sector
- // Value - cow operation
- //
- // chunk_map stores the pseudo mapping of sector
- // to COW operations. Each COW op is 4k; however,
- // we can get a read request which are as small
- // as 512 bytes. Hence, we need to binary search
- // in the chunk_map to find the nearest COW op.
- std::map<sector_t, const CowOperation*> chunk_map_;
+ // chunk_vec stores the pseudo mapping of sector
+ // to COW operations.
+ std::vector<std::pair<sector_t, const CowOperation*>> chunk_vec_;
std::mutex lock_;
diff --git a/fs_mgr/libsnapshot/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd_worker.cpp
index 16f47fe..1002569 100644
--- a/fs_mgr/libsnapshot/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_worker.cpp
@@ -185,12 +185,13 @@
return false;
}
-int WorkerThread::ReadUnalignedSector(sector_t sector, size_t size,
- std::map<sector_t, const CowOperation*>::iterator& it) {
+int WorkerThread::ReadUnalignedSector(
+ sector_t sector, size_t size,
+ std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it) {
size_t skip_sector_size = 0;
SNAP_LOG(DEBUG) << "ReadUnalignedSector: sector " << sector << " size: " << size
- << " Aligned sector: " << it->second;
+ << " Aligned sector: " << it->first;
if (!ProcessCowOp(it->second)) {
SNAP_LOG(ERROR) << "ReadUnalignedSector: " << sector << " failed of size: " << size;
@@ -223,7 +224,8 @@
*
*/
int WorkerThread::ReadData(sector_t sector, size_t size) {
- std::map<sector_t, const CowOperation*>& chunk_map = snapuserd_->GetChunkMap();
+ std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
+ std::vector<std::pair<sector_t, const CowOperation*>>::iterator it;
/*
* chunk_map stores COW operation at 4k granularity.
* If the requested IO with the sector falls on the 4k
@@ -234,10 +236,16 @@
* then we will have the find the nearest COW operation
* and chop the 4K block to fetch the requested sector.
*/
- std::map<sector_t, const CowOperation*>::iterator it = chunk_map.find(sector);
- if (it == chunk_map.end()) {
- it = chunk_map.lower_bound(sector);
- if (it != chunk_map.begin()) {
+ it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(), std::make_pair(sector, nullptr),
+ Snapuserd::compare);
+
+ CHECK(it != chunk_vec.end());
+
+ // We didn't find the required sector; hence find the previous sector
+ // as lower_bound will gives us the value greater than
+ // the requested sector
+ if (it->first != sector) {
+ if (it != chunk_vec.begin()) {
--it;
}
@@ -380,7 +388,7 @@
int WorkerThread::GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffer, loff_t offset,
int unmerged_exceptions) {
int merged_ops_cur_iter = 0;
- std::map<sector_t, const CowOperation*>& chunk_map = snapuserd_->GetChunkMap();
+ std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
// Find the operations which are merged in this cycle.
while ((unmerged_exceptions + merged_ops_cur_iter) < exceptions_per_area_) {
@@ -395,7 +403,13 @@
if (cow_de->new_chunk != 0) {
merged_ops_cur_iter += 1;
offset += sizeof(struct disk_exception);
- const CowOperation* cow_op = chunk_map[ChunkToSector(cow_de->new_chunk)];
+ auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
+ std::make_pair(ChunkToSector(cow_de->new_chunk), nullptr),
+ Snapuserd::compare);
+ CHECK(it != chunk_vec.end());
+ CHECK(it->first == ChunkToSector(cow_de->new_chunk));
+ const CowOperation* cow_op = it->second;
+
CHECK(cow_op != nullptr);
CHECK(cow_op->new_block == cow_de->old_chunk);
@@ -510,14 +524,18 @@
return true;
}
- std::map<sector_t, const CowOperation*>& chunk_map = snapuserd_->GetChunkMap();
+ std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
size_t remaining_size = header->len;
size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
CHECK(read_size == BLOCK_SZ) << "DmuserWriteRequest: read_size: " << read_size;
CHECK(header->sector > 0);
chunk_t chunk = SectorToChunk(header->sector);
- CHECK(chunk_map.find(header->sector) == chunk_map.end());
+ auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
+ std::make_pair(header->sector, nullptr), Snapuserd::compare);
+
+ bool not_found = (it == chunk_vec.end() || it->first != header->sector);
+ CHECK(not_found);
void* buffer = bufsink_.GetPayloadBuffer(read_size);
CHECK(buffer != nullptr);
@@ -550,7 +568,7 @@
size_t remaining_size = header->len;
loff_t offset = 0;
sector_t sector = header->sector;
- std::map<sector_t, const CowOperation*>& chunk_map = snapuserd_->GetChunkMap();
+ std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
do {
size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
@@ -568,8 +586,10 @@
ConstructKernelCowHeader();
SNAP_LOG(DEBUG) << "Kernel header constructed";
} else {
- if (!offset && (read_size == BLOCK_SZ) &&
- chunk_map.find(header->sector) == chunk_map.end()) {
+ auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
+ std::make_pair(header->sector, nullptr), Snapuserd::compare);
+ bool not_found = (it == chunk_vec.end() || it->first != header->sector);
+ if (!offset && (read_size == BLOCK_SZ) && not_found) {
if (!ReadDiskExceptions(chunk, read_size)) {
SNAP_LOG(ERROR) << "ReadDiskExceptions failed for chunk id: " << chunk
<< "Sector: " << header->sector;