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
This commit is contained in:
parent
49699fe895
commit
3e807e9651
4 changed files with 56 additions and 29 deletions
|
@ -250,6 +250,8 @@ bool CowReader::ParseOps(std::optional<uint64_t> label) {
|
|||
}
|
||||
|
||||
ops_ = ops_buffer;
|
||||
ops_->shrink_to_fit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -230,7 +230,7 @@ bool Snapuserd::ReadMetadata() {
|
|||
|
||||
|
||||
// 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 @@ bool Snapuserd::ReadMetadata() {
|
|||
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 @@ bool Snapuserd::ReadMetadata() {
|
|||
<< "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
|
||||
|
|
|
@ -108,7 +108,7 @@ class WorkerThread {
|
|||
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 @@ class Snapuserd : public std::enable_shared_from_this<Snapuserd> {
|
|||
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 @@ class Snapuserd : public std::enable_shared_from_this<Snapuserd> {
|
|||
// 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_;
|
||||
|
||||
|
|
|
@ -185,12 +185,13 @@ bool WorkerThread::ProcessCowOp(const CowOperation* cow_op) {
|
|||
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::ReadUnalignedSector(sector_t sector, size_t size,
|
|||
*
|
||||
*/
|
||||
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 @@ int WorkerThread::ReadData(sector_t sector, size_t size) {
|
|||
* 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 @@ loff_t WorkerThread::GetMergeStartOffset(void* merged_buffer, void* unmerged_buf
|
|||
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 @@ int WorkerThread::GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffe
|
|||
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 @@ bool WorkerThread::DmuserWriteRequest() {
|
|||
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 @@ bool WorkerThread::DmuserReadRequest() {
|
|||
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 @@ bool WorkerThread::DmuserReadRequest() {
|
|||
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;
|
||||
|
|
Loading…
Reference in a new issue