using traditional page sharing. Each unique page must be
preserved; however, we only need to store one copy of a set
of identical pages. Hence, the 52,436 nonunique pages contain only 3,577 distinct pages—implying there are roughly
14 copies of every nonunique page. Furthermore, only one
copy of the zero page is needed. In total, the 393,120 original pages can be represented by 195,224 distinct pages—a
50% savings.
The third column depicts the additional savings available
if we consider subpage sharing. Using a cut-off of 2KB for the
patch size (i.e., we do not create a patch if it will take up more
than half a page), we identify 144,497 distinct pages eligible
for patching. We store the 50,727 remaining pages as is and
use them as reference pages for the patched pages. For each
of the similar pages, we compute a patch using Xdelta. 13 The
average patch size is 1,070 bytes, allowing them to be stored
in 37,695 4KB pages, saving 106,802 pages. In sum, subpage
sharing requires only 88,422 pages to store the memory for
all VMs instead of 195,224 for fullpage sharing or 393,120
originally—an impressive 77% savings, or almost another
50% over full-page sharing. We note that this was the least
savings in our experiments; the savings from patching are
even higher in most cases. Further, a significant amount of
page sharing actually comes from zero pages and, therefore,
depends on their availability. For instance, the same workload when executed on 256MB VMs yields far fewer zero
pages. Alternative mechanisms to page sharing become
even more important in such cases.
One of the principal complications with subpage sharing is identifying candidate reference pages. Difference
Engine uses a parameterized scheme to identify similar
pages based upon the hashes of several 64-byte portions of
each page. In particular, HashSimilaritydetector(k, s) hashes
the contents of (k · s) 64-byte blocks at randomly chosen locations on the page, and then groups these hashes
together into k groups of s hashes each. We use each group
as an index into a hash table. In other words, higher values
of s capture local similarity while higher k values incorporate global similarity. Hence, HashSimilaritydetector( 1, 1)
will choose one block on a page and index that block;
pages are considered similar if that block of data matches.
HashSimilaritydetector( 1, 2) combines the hashes from two
different locations in the page into one index of length two.
HashSimilaritydetector( 2, 1) instead indexes each page twice:
once based on the contents of a first block, and again based
on the contents of a second block. Pages that match at least
one of the two blocks are chosen as candidates. For each
scheme, the number of candidates, c, specifies how many
different pages the hash table tracks for each signature.
With one candidate, we only store the first page found with
each signature; for larger values, we keep multiple pages in
the hash table for each index. When trying to build a patch,
Difference Engine computes a patch between all matching
pages and chooses the best one.
We have evaluated this scheme for a variety of parameter
settings on the two workloads described above. For both
the workloads, HashSimilaritydetector( 2, 1) with one candidate does surprisingly well. There is a substantial gain due
to hashing two distinct blocks in the page separately, but
little additional gain by hashing more blocks. Combining
blocks does not help much, at least for these workloads.
Furthermore, storing more candidates in each hash
bucket also produces little gain. Hence, Difference Engine
indexes a page by hashing 64-byte blocks at two fixed locations in the page (chosen at random) and using each hash
value as a separate index to store the page in the hash
table. To find a candidate similar page, the system computes hashes at the same two locations, looks up those
hash table entries, and chooses the better of the (at most)
two pages found there.
3. 3. compression
Finally, for pages that are not significantly similar to other
pages in memory, we consider compressing them to reduce
the memory footprint. Compression is useful only if the
compression ratio is reasonably high, and, like patching,
if selected pages are accessed infrequently, otherwise the
overhead of compression/decompression will outweigh
the benefits. We identify candidate pages for compression
using a global clock algorithm (Section 4. 2), assuming that
pages that have not been recently accessed are unlikely to be
accessed in the near future.
Difference Engine supports multiple compression
algorithms, currently LZO and WKdm as described in
Wilson et al. 20; we invalidate compressed pages in the VM
and save them in a dynamically allocated storage area in
machine memory. When a VM accesses a compressed page,
Difference Engine decompresses the page and returns it to
the VM uncompressed. It remains there until it is again considered for compression.
3. 4. Paging machine memory
While Difference Engine will deliver some (typically high)
level of memory savings, in the worst case all VMs might
actually require all of their allocated memory. Setting aside
sufficient physical memory to account for this case prevents
using the memory saved by Difference Engine to create additional VMs. Not doing so, however, may result in temporarily
overshooting the physical memory capacity of the machine
and cause a system crash. We therefore require a demand-paging mechanism to supplement main memory by writing
pages out to disk in such cases.
A good candidate page for swapping out would likely not
be accessed in the near future—the same requirement as
compressed/patched pages. In fact, Difference Engine also
considers compressed and patched pages as candidates
for swapping out. Once the contents of the page are written
to disk, the page can be reclaimed. When a VM accesses a
swapped out page, Difference Engine fetches it from disk
and copies the contents into a newly allocated page that is
mapped appropriately in the VM’s memory.
Since disk I/O is involved, swapping in/out is an expensive operation. Further, a swapped page is unavailable for
sharing or as a reference page for patching. Therefore, swapping should be an infrequent operation. Difference Engine
implements the core mechanisms for paging, and leaves
policy decisions—such as when and how much to swap—to
user space tools.