--- /dev/null 2018-04-03 12:55:20.301839954 +0200 +++ new/src/hotspot/share/gc/z/zPageCache.cpp 2018-06-08 19:46:29.844139241 +0200 @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "precompiled.hpp" +#include "gc/z/zList.inline.hpp" +#include "gc/z/zNUMA.hpp" +#include "gc/z/zPage.inline.hpp" +#include "gc/z/zPageCache.hpp" +#include "gc/z/zStat.hpp" +#include "logging/log.hpp" + +static const ZStatCounter ZCounterPageCacheHitL1("Memory", "Page Cache Hit L1", ZStatUnitOpsPerSecond); +static const ZStatCounter ZCounterPageCacheHitL2("Memory", "Page Cache Hit L2", ZStatUnitOpsPerSecond); +static const ZStatCounter ZCounterPageCacheMiss("Memory", "Page Cache Miss", ZStatUnitOpsPerSecond); +static const ZStatCounter ZCounterPageCacheFlush("Memory", "Page Cache Flush", ZStatUnitBytesPerSecond); + +ZPageCache::ZPageCache() : + _available(0), + _small(), + _medium(), + _large() {} + +ZPage* ZPageCache::alloc_small_page() { + const uint32_t numa_id = ZNUMA::id(); + const uint32_t numa_count = ZNUMA::count(); + + // Try NUMA local page cache + ZPage* const l1_page = _small.get(numa_id).remove_first(); + if (l1_page != NULL) { + ZStatInc(ZCounterPageCacheHitL1); + return l1_page; + } + + // Try NUMA remote page cache(s) + uint32_t remote_numa_id = numa_id + 1; + const uint32_t remote_numa_count = numa_count - 1; + for (uint32_t i = 0; i < remote_numa_count; i++) { + if (remote_numa_id == numa_count) { + remote_numa_id = 0; + } + + ZPage* const l2_page = _small.get(remote_numa_id).remove_first(); + if (l2_page != NULL) { + ZStatInc(ZCounterPageCacheHitL2); + return l2_page; + } + + remote_numa_id++; + } + + ZStatInc(ZCounterPageCacheMiss); + return NULL; +} + +ZPage* ZPageCache::alloc_medium_page() { + ZPage* const l1_page = _medium.remove_first(); + if (l1_page != NULL) { + ZStatInc(ZCounterPageCacheHitL1); + return l1_page; + } + + ZStatInc(ZCounterPageCacheMiss); + return NULL; +} + +ZPage* ZPageCache::alloc_large_page(size_t size) { + // Find a page with the right size + ZListIterator iter(&_large); + for (ZPage* l1_page; iter.next(&l1_page);) { + if (l1_page->size() == size) { + // Page found + _large.remove(l1_page); + ZStatInc(ZCounterPageCacheHitL1); + return l1_page; + } + } + + ZStatInc(ZCounterPageCacheMiss); + return NULL; +} + +ZPage* ZPageCache::alloc_page(uint8_t type, size_t size) { + ZPage* page; + + if (type == ZPageTypeSmall) { + page = alloc_small_page(); + } else if (type == ZPageTypeMedium) { + page = alloc_medium_page(); + } else { + page = alloc_large_page(size); + } + + if (page != NULL) { + _available -= page->size(); + } + + return page; +} + +void ZPageCache::free_page(ZPage* page) { + assert(!page->is_active(), "Invalid page state"); + assert(!page->is_pinned(), "Invalid page state"); + assert(!page->is_detached(), "Invalid page state"); + + const uint8_t type = page->type(); + if (type == ZPageTypeSmall) { + _small.get(page->numa_id()).insert_first(page); + } else if (type == ZPageTypeMedium) { + _medium.insert_first(page); + } else { + _large.insert_first(page); + } + + _available += page->size(); +} + +void ZPageCache::flush_list(ZList* from, size_t requested, ZList* to, size_t* flushed) { + while (*flushed < requested) { + // Flush least recently used + ZPage* const page = from->remove_last(); + if (page == NULL) { + break; + } + + *flushed += page->size(); + to->insert_last(page); + } +} + +void ZPageCache::flush_per_numa_lists(ZPerNUMA >* from, size_t requested, ZList* to, size_t* flushed) { + const uint32_t numa_count = ZNUMA::count(); + uint32_t numa_empty = 0; + uint32_t numa_next = 0; + + // Flush lists round-robin + while (*flushed < requested) { + ZPage* const page = from->get(numa_next).remove_last(); + + if (++numa_next == numa_count) { + numa_next = 0; + } + + if (page == NULL) { + // List is empty + if (++numa_empty == numa_count) { + // All lists are empty + break; + } + + // Try next list + continue; + } + + // Flush page + numa_empty = 0; + *flushed += page->size(); + to->insert_last(page); + } +} + +void ZPageCache::flush(ZList* to, size_t requested) { + size_t flushed = 0; + + // Prefer flushing large, then medium and last small pages + flush_list(&_large, requested, to, &flushed); + flush_list(&_medium, requested, to, &flushed); + flush_per_numa_lists(&_small, requested, to, &flushed); + + ZStatInc(ZCounterPageCacheFlush, flushed); + + log_info(gc, heap)("Page Cache Flushed: " + SIZE_FORMAT "M requested, " + SIZE_FORMAT "M(" SIZE_FORMAT "M->" SIZE_FORMAT "M) flushed", + requested / M, flushed / M , _available / M, (_available - flushed) / M); + + _available -= flushed; +}