Pack memory in state image, reduces memory use during state loading

This commit is contained in:
Fabian 2018-10-31 11:50:33 -06:00
parent 32606a26ac
commit 58b8c49fb0
2 changed files with 118 additions and 7 deletions

View file

@ -419,7 +419,6 @@ CPU.prototype.get_state = function()
state[39] = this.reg32s;
state[40] = this.sreg;
state[41] = this.dreg;
state[42] = this.mem8;
this.store_current_tsc();
state[43] = this.current_tsc;
@ -463,12 +462,22 @@ CPU.prototype.get_state = function()
state[76] = this.fxsave_store_fpu_mask;
const { packed_memory, bitmap } = this.pack_memory();
state[77] = packed_memory;
state[78] = new Uint8Array(bitmap.get_buffer());
return state;
};
CPU.prototype.set_state = function(state)
{
this.memory_size[0] = state[0];
if(this.mem8.length !== this.memory_size[0])
{
console.warn("Note: Memory size mismatch. we=" + this.mem8.length + " state=" + this.memory_size[0]);
}
this.segment_is_null.set(state[1]);
this.segment_offsets.set(state[2]);
this.segment_limits.set(state[3]);
@ -508,12 +517,6 @@ CPU.prototype.set_state = function(state)
this.sreg.set(state[40]);
this.dreg.set(state[41]);
if(this.mem8.length !== state[42].length)
{
console.warn("Note: Memory size mismatch. we=" + this.mem8.length + " state=" + state[42].length);
}
this.mem8.set(state[42]);
this.set_tsc(state[43][0], state[43][1]);
this.devices.virtio_9p = state[45];
@ -555,11 +558,77 @@ CPU.prototype.set_state = function(state)
this.fxsave_store_fpu_mask = state[76];
const bitmap = new v86util.Bitmap(state[78].buffer);
const packed_memory = state[77];
this.unpack_memory(bitmap, packed_memory);
this.full_clear_tlb();
this.update_operand_size();
};
CPU.prototype.pack_memory = function()
{
dbg_assert((this.mem8.length & 0xFFF) === 0);
const page_count = this.mem8.length >> 12;
const nonzero_pages = [];
for(let page = 0; page < page_count; page++)
{
const offset = page << 12;
const view = this.mem32s.subarray(offset >> 2, offset + 0x1000 >> 2);
let is_zero = true;
for(let i = 0; i < view.length; i++)
{
if(view[i] !== 0)
{
is_zero = false;
break;
}
}
if(!is_zero)
{
nonzero_pages.push(page);
}
}
const bitmap = new v86util.Bitmap(page_count);
const packed_memory = new Uint8Array(nonzero_pages.length << 12);
for(let [i, page] of nonzero_pages.entries())
{
bitmap.set(page, 1);
const offset = page << 12;
const page_contents = this.mem8.subarray(offset, offset + 0x1000);
packed_memory.set(page_contents, i << 12);
}
return { bitmap, packed_memory };
};
CPU.prototype.unpack_memory = function(bitmap, packed_memory)
{
// TODO: Skip zeroing memory if the memory has just been allocated
this.mem8.fill(0);
const page_count = this.memory_size[0] >> 12;
let packed_page = 0;
for(let page = 0; page < page_count; page++)
{
if(bitmap.get(page))
{
let offset = packed_page << 12;
let view = packed_memory.subarray(offset, offset + 0x1000);
this.mem8.set(view, page << 12);
packed_page++;
}
}
};
/**
* @return {number} time in ms until this method should becalled again

View file

@ -336,3 +336,45 @@ CircularQueue.prototype.set = function(new_data)
this.data = new_data;
this.index = 0;
};
/**
* A simple 1d bitmap
*/
v86util.Bitmap = function(length_or_buffer)
{
if(typeof length_or_buffer === "number")
{
this.view = new Uint8Array(length_or_buffer + 7 >> 3);
}
else if(length_or_buffer instanceof ArrayBuffer)
{
this.view = new Uint8Array(length_or_buffer);
}
else
{
console.assert(false);
}
};
v86util.Bitmap.prototype.set = function(index, value)
{
const bit_index = index & 7;
const byte_index = index >> 3;
const bit_mask = 1 << bit_index;
this.view[byte_index] =
value ? this.view[byte_index] | bit_mask : this.view[byte_index] & ~bit_mask;
};
v86util.Bitmap.prototype.get = function(index)
{
const bit_index = index & 7;
const byte_index = index >> 3;
return this.view[byte_index] >> bit_index & 1;
};
v86util.Bitmap.prototype.get_buffer = function()
{
return this.view.buffer;
};