kvm: optimize GFN to memslot lookup with large slots amount
authorIgor Mammedov <imammedo@redhat.com>
Mon, 1 Dec 2014 17:29:27 +0000 (17:29 +0000)
committerPaolo Bonzini <pbonzini@redhat.com>
Thu, 4 Dec 2014 14:29:11 +0000 (15:29 +0100)
Current linear search doesn't scale well when
large amount of memslots is used and looked up slot
is not in the beginning memslots array.
Taking in account that memslots don't overlap, it's
possible to switch sorting order of memslots array from
'npages' to 'base_gfn' and use binary search for
memslot lookup by GFN.

As result of switching to binary search lookup times
are reduced with large amount of memslots.

Following is a table of search_memslot() cycles
during WS2008R2 guest boot.

                         boot,          boot + ~10 min
                         mostly same    of using it,
                         slot lookup    randomized lookup
                max      average        average
                cycles   cycles         cycles

13 slots      : 1450       28           30

13 slots      : 1400       30           40
binary search

117 slots     : 13000      30           460

117 slots     : 2000       35           180
binary search

Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
include/linux/kvm_host.h
virt/kvm/kvm_main.c

index 1a371447fd45b8d390af0339fe86343311c2522c..193bca68372d3a73f08dd39288fa84a5a95df1b3 100644 (file)
@@ -354,6 +354,7 @@ struct kvm_memslots {
        /* The mapping table from slot id to the index in memslots[]. */
        short id_to_index[KVM_MEM_SLOTS_NUM];
        atomic_t lru_slot;
+       int used_slots;
 };
 
 struct kvm {
@@ -791,19 +792,28 @@ static inline void kvm_guest_exit(void)
 static inline struct kvm_memory_slot *
 search_memslots(struct kvm_memslots *slots, gfn_t gfn)
 {
+       int start = 0, end = slots->used_slots;
        int slot = atomic_read(&slots->lru_slot);
-       struct kvm_memory_slot *memslot = &slots->memslots[slot];
-
-       if (gfn >= memslot->base_gfn &&
-           gfn < memslot->base_gfn + memslot->npages)
-               return memslot;
-
-       kvm_for_each_memslot(memslot, slots)
-               if (gfn >= memslot->base_gfn &&
-                     gfn < memslot->base_gfn + memslot->npages) {
-                       atomic_set(&slots->lru_slot, memslot - slots->memslots);
-                       return memslot;
-               }
+       struct kvm_memory_slot *memslots = slots->memslots;
+
+       if (gfn >= memslots[slot].base_gfn &&
+           gfn < memslots[slot].base_gfn + memslots[slot].npages)
+               return &memslots[slot];
+
+       while (start < end) {
+               slot = start + (end - start) / 2;
+
+               if (gfn >= memslots[slot].base_gfn)
+                       end = slot;
+               else
+                       start = slot + 1;
+       }
+
+       if (gfn >= memslots[start].base_gfn &&
+           gfn < memslots[start].base_gfn + memslots[start].npages) {
+               atomic_set(&slots->lru_slot, start);
+               return &memslots[start];
+       }
 
        return NULL;
 }
index 162817f853ec6f0e443fbd8d5fc9610281c2a76e..759af6596a07ec10fd3e54f5c49e8b195a195df0 100644 (file)
@@ -679,8 +679,14 @@ static void update_memslots(struct kvm_memslots *slots,
        struct kvm_memory_slot *mslots = slots->memslots;
 
        WARN_ON(mslots[i].id != id);
-       if (!new->npages)
+       if (!new->npages) {
                new->base_gfn = 0;
+               if (mslots[i].npages)
+                       slots->used_slots--;
+       } else {
+               if (!mslots[i].npages)
+                       slots->used_slots++;
+       }
 
        while (i < KVM_MEM_SLOTS_NUM - 1 &&
               new->base_gfn <= mslots[i + 1].base_gfn) {