mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 19:27:45 +00:00
Kernel: Optimize VM range deallocation a bit
Previously, when deallocating a range of VM, we would sort and merge the range list. This was quite slow for large processes. This patch optimizes VM deallocation in the following ways: - Use binary search instead of linear scan to find the place to insert the deallocated range. - Insert at the right place immediately, removing the need to sort. - Merge the inserted range with any adjacent range(s) in-line instead of doing a separate merge pass into a list copy. - Add Traits<Range> to inform Vector that Range objects are trivial and can be moved using memmove(). I've also added an assertion that deallocated ranges are actually part of the RangeAllocator's initial address range. I've benchmarked this using g++ to compile Kernel/Process.cpp. With these changes, compilation goes from ~41 sec to ~35 sec.
This commit is contained in:
parent
502626eecb
commit
ad3f931707
4 changed files with 53 additions and 31 deletions
|
@ -26,6 +26,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/StdLibExtras.h>
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
|
|
||||||
namespace AK {
|
namespace AK {
|
||||||
|
@ -37,7 +38,7 @@ int integral_compare(const T& a, const T& b)
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename Compare>
|
template<typename T, typename Compare>
|
||||||
T* binary_search(T* haystack, size_t haystack_size, const T& needle, Compare compare = integral_compare)
|
T* binary_search(T* haystack, size_t haystack_size, const T& needle, Compare compare = integral_compare, int* nearby_index = nullptr)
|
||||||
{
|
{
|
||||||
int low = 0;
|
int low = 0;
|
||||||
int high = haystack_size - 1;
|
int high = haystack_size - 1;
|
||||||
|
@ -48,9 +49,15 @@ T* binary_search(T* haystack, size_t haystack_size, const T& needle, Compare com
|
||||||
high = middle - 1;
|
high = middle - 1;
|
||||||
else if (comparison > 0)
|
else if (comparison > 0)
|
||||||
low = middle + 1;
|
low = middle + 1;
|
||||||
else
|
else {
|
||||||
|
if (nearby_index)
|
||||||
|
*nearby_index = middle;
|
||||||
return &haystack[middle];
|
return &haystack[middle];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nearby_index)
|
||||||
|
*nearby_index = max(0, min(low, high));
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -359,15 +359,19 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename C>
|
template<typename C>
|
||||||
void insert_before_matching(T&& value, C callback)
|
void insert_before_matching(T&& value, C callback, int first_index = 0, int* inserted_index = nullptr)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < size(); ++i) {
|
for (int i = first_index; i < size(); ++i) {
|
||||||
if (callback(at(i))) {
|
if (callback(at(i))) {
|
||||||
insert(i, move(value));
|
insert(i, move(value));
|
||||||
|
if (inserted_index)
|
||||||
|
*inserted_index = i;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
append(move(value));
|
append(move(value));
|
||||||
|
if (inserted_index)
|
||||||
|
*inserted_index = size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector& operator=(const Vector& other)
|
Vector& operator=(const Vector& other)
|
||||||
|
|
|
@ -24,10 +24,12 @@
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <AK/BinarySearch.h>
|
||||||
#include <AK/QuickSort.h>
|
#include <AK/QuickSort.h>
|
||||||
#include <Kernel/Random.h>
|
#include <Kernel/Random.h>
|
||||||
#include <Kernel/VM/RangeAllocator.h>
|
#include <Kernel/VM/RangeAllocator.h>
|
||||||
#include <Kernel/kstdio.h>
|
#include <Kernel/kstdio.h>
|
||||||
|
#include <Kernel/Thread.h>
|
||||||
|
|
||||||
//#define VRA_DEBUG
|
//#define VRA_DEBUG
|
||||||
#define VM_GUARD_PAGES
|
#define VM_GUARD_PAGES
|
||||||
|
@ -38,6 +40,7 @@ RangeAllocator::RangeAllocator()
|
||||||
|
|
||||||
void RangeAllocator::initialize_with_range(VirtualAddress base, size_t size)
|
void RangeAllocator::initialize_with_range(VirtualAddress base, size_t size)
|
||||||
{
|
{
|
||||||
|
m_total_range = { base, size };
|
||||||
m_available_ranges.append({ base, size });
|
m_available_ranges.append({ base, size });
|
||||||
#ifdef VRA_DEBUG
|
#ifdef VRA_DEBUG
|
||||||
dump();
|
dump();
|
||||||
|
@ -46,6 +49,7 @@ void RangeAllocator::initialize_with_range(VirtualAddress base, size_t size)
|
||||||
|
|
||||||
void RangeAllocator::initialize_from_parent(const RangeAllocator& parent_allocator)
|
void RangeAllocator::initialize_from_parent(const RangeAllocator& parent_allocator)
|
||||||
{
|
{
|
||||||
|
m_total_range = parent_allocator.m_total_range;
|
||||||
m_available_ranges = parent_allocator.m_available_ranges;
|
m_available_ranges = parent_allocator.m_available_ranges;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,42 +150,40 @@ Range RangeAllocator::allocate_specific(VirtualAddress base, size_t size)
|
||||||
|
|
||||||
void RangeAllocator::deallocate(Range range)
|
void RangeAllocator::deallocate(Range range)
|
||||||
{
|
{
|
||||||
|
ASSERT(m_total_range.contains(range));
|
||||||
|
|
||||||
#ifdef VRA_DEBUG
|
#ifdef VRA_DEBUG
|
||||||
dbgprintf("VRA: Deallocate: %x(%u)\n", range.base().get(), range.size());
|
dbgprintf("VRA: Deallocate: %x(%u)\n", range.base().get(), range.size());
|
||||||
dump();
|
dump();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (auto& available_range : m_available_ranges) {
|
ASSERT(!m_available_ranges.is_empty());
|
||||||
if (available_range.end() == range.base()) {
|
|
||||||
available_range.m_size += range.size();
|
|
||||||
goto sort_and_merge;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_available_ranges.append(range);
|
|
||||||
|
|
||||||
sort_and_merge:
|
int nearby_index = 0;
|
||||||
// FIXME: We don't have to sort if we insert at the right position immediately.
|
auto* existing_range = binary_search(m_available_ranges.data(), m_available_ranges.size(), range, [](auto& a, auto& b) {
|
||||||
quick_sort(m_available_ranges.begin(), m_available_ranges.end(), [](auto& a, auto& b) {
|
return a.base().get() - b.end().get();
|
||||||
return a.base() < b.base();
|
}, &nearby_index);
|
||||||
});
|
|
||||||
|
|
||||||
Vector<Range> merged_ranges;
|
int inserted_index = 0;
|
||||||
merged_ranges.ensure_capacity(m_available_ranges.size());
|
if (existing_range) {
|
||||||
|
existing_range->m_size += range.size();
|
||||||
for (auto& range : m_available_ranges) {
|
inserted_index = nearby_index;
|
||||||
if (merged_ranges.is_empty()) {
|
} else {
|
||||||
merged_ranges.append(range);
|
m_available_ranges.insert_before_matching(Range(range), [&](auto& entry) {
|
||||||
continue;
|
return entry.base() < range.end();
|
||||||
}
|
}, nearby_index, &inserted_index);
|
||||||
if (range.base() == merged_ranges.last().end()) {
|
|
||||||
merged_ranges.last().m_size += range.size();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
merged_ranges.append(range);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_available_ranges = move(merged_ranges);
|
if (inserted_index < (m_available_ranges.size() - 1)) {
|
||||||
|
// We already merged with previous. Try to merge with next.
|
||||||
|
auto& inserted_range = m_available_ranges[inserted_index];
|
||||||
|
auto& next_range = m_available_ranges[inserted_index + 1];
|
||||||
|
if (inserted_range.end() == next_range.base()) {
|
||||||
|
inserted_range.m_size += next_range.size();
|
||||||
|
m_available_ranges.remove(inserted_index + 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
#ifdef VRA_DEBUG
|
#ifdef VRA_DEBUG
|
||||||
dbgprintf("VRA: After deallocate\n");
|
dbgprintf("VRA: After deallocate\n");
|
||||||
dump();
|
dump();
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
|
#include <AK/Traits.h>
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
#include <Kernel/VM/VirtualAddress.h>
|
#include <Kernel/VM/VirtualAddress.h>
|
||||||
|
|
||||||
|
@ -89,9 +90,17 @@ private:
|
||||||
void carve_at_index(int, const Range&);
|
void carve_at_index(int, const Range&);
|
||||||
|
|
||||||
Vector<Range> m_available_ranges;
|
Vector<Range> m_available_ranges;
|
||||||
|
Range m_total_range;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline const LogStream& operator<<(const LogStream& stream, const Range& value)
|
inline const LogStream& operator<<(const LogStream& stream, const Range& value)
|
||||||
{
|
{
|
||||||
return stream << String::format("Range(%x-%x)", value.base().get(), value.end().get() - 1);
|
return stream << String::format("Range(%x-%x)", value.base().get(), value.end().get() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace AK {
|
||||||
|
template<>
|
||||||
|
struct Traits<Range> : public GenericTraits<Range> {
|
||||||
|
static constexpr bool is_trivial() { return true; }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue