From: wessels <> Date: Fri, 25 Jun 1999 02:19:53 +0000 (+0000) Subject: committing Dilley's HEAP replacement code X-Git-Tag: SQUID_3_0_PRE1~2144 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=2b906e482d2956fa5ba5cb10272c3a6073a62f50;p=thirdparty%2Fsquid.git committing Dilley's HEAP replacement code --- diff --git a/CONTRIBUTORS b/CONTRIBUTORS index cdea6e9566..30cb715cbe 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -69,6 +69,7 @@ and ideas to make this software available. Michael Lupp Niall Doherty Pedro Ribeiro + John Dilley Development of this caching software is funded by the National Science Foundation (grants NCR-9616602 and NCR-9521745). Paid staff members on diff --git a/lib/Makefile.in b/lib/Makefile.in index 2645c5947a..bd42b6234d 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -1,5 +1,5 @@ # -# $Id: Makefile.in,v 1.43 1999/05/04 21:01:04 wessels Exp $ +# $Id: Makefile.in,v 1.44 1999/06/24 20:19:55 wessels Exp $ # prefix = @prefix@ top_srcdir = @top_srcdir@ @@ -38,6 +38,7 @@ UTILOBJS = rfc1123.o \ Array.o \ Stack.o \ hash.o \ + heap.o \ $(LIBOBJS) REGEXOBJS = GNUregex.o DLMALLOCOBJS = dlmalloc.o diff --git a/src/cache_cf.cc b/src/cache_cf.cc index a36f0b1b41..850943404c 100644 --- a/src/cache_cf.cc +++ b/src/cache_cf.cc @@ -1,6 +1,6 @@ /* - * $Id: cache_cf.cc,v 1.332 1999/05/27 02:42:31 wessels Exp $ + * $Id: cache_cf.cc,v 1.333 1999/06/24 20:19:56 wessels Exp $ * * DEBUG: section 3 Configuration File Parsing * AUTHOR: Harvest Derived @@ -306,10 +306,14 @@ configDoConfigure(void) debug(3, 0) ("WARNING: resetting 'maximum_single_addr_tries to 1\n"); Config.retry.maxtries = 1; } +#if HEAP_REPLACEMENT + /* The non-LRU policies do not use referenceAge */ +#else if (Config.referenceAge < 300) { debug(3, 0) ("WARNING: resetting 'reference_age' to 1 week\n"); Config.referenceAge = 86400 * 7; } +#endif requirePathnameExists("MIME Config Table", Config.mimeTablePathname); requirePathnameExists("cache_dns_program", Config.Program.dnsserver); requirePathnameExists("unlinkd_program", Config.Program.unlinkd); diff --git a/src/cf.data.pre b/src/cf.data.pre index 9afafce6ad..fc5cc0722e 100644 --- a/src/cf.data.pre +++ b/src/cf.data.pre @@ -1,6 +1,6 @@ # -# $Id: cf.data.pre,v 1.157 1999/06/16 22:10:36 wessels Exp $ +# $Id: cf.data.pre,v 1.158 1999/06/24 20:19:58 wessels Exp $ # # # SQUID Internet Object Cache http://squid.nlanr.net/Squid/ @@ -514,12 +514,17 @@ TYPE: int DEFAULT: 95 LOC: Config.Swap.highWaterMark DOC_START - The low- and high-water marks for cache LRU replacement. LRU - replacement begins when the high-water mark is reached and ends - when enough objects have been removed and the low-water mark is - reached. Defaults are 90% and 95%. If you have a large cache, 5% - could be hundreds of MB. If this is the case you may wish to - set these numbers closer together. + + The low- and high-water marks for cache object replacement. + Replacement begins when the swap (disk) usage is above the + low-water mark and attempts to maintain utilization near the + low-water mark. As swap utilization gets close to high-water + mark object eviction becomes more aggressive. If utilization is + close to the low-water mark less replacement is done each time. + + Defaults are 90% and 95%. If you have a large cache, 5% could be + hundreds of MB. If this is the case you may wish to set these + numbers closer together. cache_swap_low 90 cache_swap_high 95 @@ -538,6 +543,10 @@ DOC_START hits). If you wish to increase speed more than your want to save bandwidth you should leave this low. + NOTE: if using the LFUDA replacement policy you should increase + this value to maximize the byte hit rate improvement of LFUDA! + See replacement_policy below for a discussion of this policy. + maximum_object_size 4096 KB DOC_END @@ -1147,6 +1156,50 @@ NOCOMMENT_END DOC_END +NAME: replacement_policy +TYPE: string +LOC: Config.replPolicy +DEFAULT: LFUDA +IFDEF: HEAP_REPLACEMENT +DOC_START + The cache replacement policy parameter determines which + objects are evicted (replaced) when disk space is needed. + Squid used to have only a single replacement policy, LRU. + But when built with -DHEAP_REPLACEMENT you can choose + between two new, enhanced policies: + + GDSF: Greedy-Dual Size Frequency + LFUDA: Least Frequently Used with Dynamic Aging + + Both of these policies are frequency based rather than recency + based, and perform better than LRU. + + The GDSF policy optimizes object hit rate by keeping smaller + popular objects in cache so it has a better chance of getting a + hit. It achieves a lower byte hit rate than LFUDA though since + it evicts larger (possibly popular) objects. + + The LFUDA policy keeps popular objects in cache regardless of + their size and thus optimizes byte hit rate at the expense of + hit rate since one large, popular object will prevent many + smaller, slightly less popular objects from being cached. + + Both policies utilize a dynamic aging mechanism that prevents + cache pollution that can otherwise occur with frequency-based + replacement policies. + + NOTE: if using the LFUDA replacement policy you should increase + the value of maximum_object_size above its default of 4096 KB to + to maximize the potential byte hit rate improvement of LFUDA. + + For more information about these cache replacement policies see + http://www.hpl.hp.com/personal/John_Dilley/caching/wcw.html and + http://fog.hpl.external.hp.com/techreports/98/HPL-98-173.html. + +replacement_policy LFUDA +DOC_END + + NAME: reference_age TYPE: time_t LOC: Config.referenceAge @@ -1169,6 +1222,9 @@ DOC_START 4 months 2.2 hours + NOTE: this parameter is not used when using the enhanced + replacement policies, GDSH or LFUDA. + reference_age 1 year DOC_END diff --git a/src/client_side.cc b/src/client_side.cc index 6acd06a4b0..dfc4dcc514 100644 --- a/src/client_side.cc +++ b/src/client_side.cc @@ -1,6 +1,6 @@ /* - * $Id: client_side.cc,v 1.457 1999/05/21 22:16:56 wessels Exp $ + * $Id: client_side.cc,v 1.458 1999/06/24 20:20:01 wessels Exp $ * * DEBUG: section 33 Client-side Routines * AUTHOR: Duane Wessels @@ -320,6 +320,9 @@ clientProcessExpired(void *data) entry->lastmod = http->old_entry->lastmod; debug(33, 5) ("clientProcessExpired: lastmod %d\n", (int) entry->lastmod); entry->refcount++; /* EXPIRED CASE */ +#if HEAP_REPLACEMENT + storeHeapPositionUpdate(entry); +#endif http->entry = entry; http->out.offset = 0; fwdStart(http->conn->fd, http->entry, http->request, @@ -405,6 +408,9 @@ clientHandleIMSReply(void *data, char *buf, ssize_t size) storeUnlockObject(entry); entry = http->entry = http->old_entry; entry->refcount++; +#if HEAP_REPLACEMENT + storeHeapPositionUpdate(entry); +#endif } else if (STORE_PENDING == entry->store_status && 0 == status) { debug(33, 3) ("clientHandleIMSReply: Incomplete headers for '%s'\n", url); if (size >= CLIENT_SOCK_SZ) { @@ -416,6 +422,9 @@ clientHandleIMSReply(void *data, char *buf, ssize_t size) storeUnlockObject(entry); entry = http->entry = http->old_entry; entry->refcount++; +#if HEAP_REPLACEMENT + storeHeapPositionUpdate(entry); +#endif /* continue */ } else { storeClientCopy(entry, @@ -459,6 +468,9 @@ clientHandleIMSReply(void *data, char *buf, ssize_t size) mem->reply); storeTimestampsSet(http->old_entry); http->old_entry->refcount++; +#if HEAP_REPLACEMENT + storeHeapPositionUpdate(http->old_entry); +#endif http->log_type = LOG_TCP_REFRESH_HIT; } storeUnregister(http->old_entry, http); @@ -1913,6 +1925,9 @@ clientProcessRequest(clientHttpRequest * http) delaySetStoreClient(http->entry, http, delayClient(r)); #endif http->entry->refcount++; +#if HEAP_REPLACEMENT + storeHeapPositionUpdate(http->entry); +#endif storeClientCopy(http->entry, http->out.offset, http->out.offset, @@ -1968,6 +1983,9 @@ clientProcessMiss(clientHttpRequest * http) assert(http->out.offset == 0); http->entry = clientCreateStoreEntry(http, r->method, r->flags); http->entry->refcount++; +#if HEAP_REPLACEMENT + storeHeapPositionUpdate(http->entry); +#endif if (http->redirect.status) { HttpReply *rep = httpReplyCreate(); storeReleaseRequest(http->entry); diff --git a/src/globals.h b/src/globals.h index 0a897a1dab..efd02a816d 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1,6 +1,6 @@ /* - * $Id: globals.h,v 1.83 1999/06/11 23:30:36 glenn Exp $ + * $Id: globals.h,v 1.84 1999/06/24 20:20:03 wessels Exp $ * * * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ @@ -129,7 +129,12 @@ extern double request_failure_ratio; /* 0.0 */ extern double current_dtime; extern int store_hash_buckets; /* 0 */ extern hash_table *store_table; /* NULL */ +#if HEAP_REPLACEMENT +extern heap *store_heap; +extern heap *inmem_heap; +#else extern dlink_list store_list; +#endif extern dlink_list ClientActiveRequests; extern const String StringNull; /* { 0, 0, NULL } */ extern const MemBuf MemBufNull; /* MemBufNULL */ diff --git a/src/protos.h b/src/protos.h index afe2a881c1..dc8a9abc37 100644 --- a/src/protos.h +++ b/src/protos.h @@ -1,6 +1,6 @@ /* - * $Id: protos.h,v 1.339 1999/06/10 21:06:24 wessels Exp $ + * $Id: protos.h,v 1.340 1999/06/24 20:20:05 wessels Exp $ * * * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ @@ -855,6 +855,7 @@ extern int contentLen(const StoreEntry * e); extern HttpReply *storeEntryReply(StoreEntry *); extern int storeTooManyDiskFilesOpen(void); extern void storeEntryReset(StoreEntry *); +extern void storeHeapPositionUpdate(StoreEntry *); /* store_io.c */ extern STOBJOPEN storeOpen; diff --git a/src/squid.h b/src/squid.h index 8b734d85fd..fb8727b9ad 100644 --- a/src/squid.h +++ b/src/squid.h @@ -1,6 +1,6 @@ /* - * $Id: squid.h,v 1.192 1999/05/04 21:58:36 wessels Exp $ + * $Id: squid.h,v 1.193 1999/06/24 20:20:07 wessels Exp $ * * AUTHOR: Duane Wessels * @@ -345,6 +345,9 @@ struct rusage { #include "hash.h" #include "rfc1035.h" +#if HEAP_REPLACEMENT +#include "heap.h" +#endif #include "defines.h" #include "enums.h" diff --git a/src/stat.cc b/src/stat.cc index d269179da5..1ac30ecff4 100644 --- a/src/stat.cc +++ b/src/stat.cc @@ -1,6 +1,6 @@ /* - * $Id: stat.cc,v 1.318 1999/05/22 02:28:56 wessels Exp $ + * $Id: stat.cc,v 1.319 1999/06/24 20:20:08 wessels Exp $ * * DEBUG: section 18 Cache Manager Statistics * AUTHOR: Harvest Derived @@ -488,8 +488,12 @@ info_get(StoreEntry * sentry) store_swap_size); storeAppendPrintf(sentry, "\tStorage Mem size:\t%d KB\n", (int) (store_mem_size >> 10)); +#if HEAP_REPLACEMENT + /* The non-LRU policies do not use referenceAge */ +#else storeAppendPrintf(sentry, "\tStorage LRU Expiration Age:\t%6.2f days\n", (double) storeExpiredReferenceAge() / 86400.0); +#endif storeAppendPrintf(sentry, "\tMean Object Size:\t%0.2f KB\n", n_disk_objects ? (double) store_swap_size / n_disk_objects : 0.0); storeAppendPrintf(sentry, "\tRequests given to unlinkd:\t%d\n", diff --git a/src/store.cc b/src/store.cc index 12dffddce9..2dc30a8c6c 100644 --- a/src/store.cc +++ b/src/store.cc @@ -1,6 +1,6 @@ /* - * $Id: store.cc,v 1.503 1999/05/26 04:36:55 wessels Exp $ + * $Id: store.cc,v 1.504 1999/06/24 20:20:12 wessels Exp $ * * DEBUG: section 20 Storage Manager * AUTHOR: Harvest Derived @@ -21,12 +21,12 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program 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 for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. @@ -87,11 +87,21 @@ static int getKeyCounter(void); static int storeKeepInMemory(const StoreEntry *); static OBJH storeCheckCachableStats; static EVH storeLateRelease; +static heap_key_func HeapKeyGen_StoreEntry_LFUDA; +static heap_key_func HeapKeyGen_StoreEntry_GDSF; +static heap_key_func HeapKeyGen_StoreEntry_LRU; /* * local variables */ +#if HEAP_REPLACEMENT +/* + * The heap equivalent of inmem_list, inmem_heap, is in globals.c so other + * modules can access it when updating object metadata (e.g., refcount) + */ +#else static dlink_list inmem_list; +#endif static int store_pages_max = 0; static int store_swap_high = 0; static int store_swap_low = 0; @@ -171,14 +181,31 @@ storeHashInsert(StoreEntry * e, const cache_key * key) e, storeKeyText(key)); e->key = storeKeyDup(key); hash_join(store_table, (hash_link *) e); +#if HEAP_REPLACEMENT + if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) { + debug(20, 4) ("storeHashInsert: not inserting special into store heap\n"); + } else { + e->node = heap_insert(store_heap, e); + debug(20, 4) ("storeHashInsert: inserted node 0x%x\n", e->node); + } +#else dlinkAdd(e, &e->lru, &store_list); +#endif } static void storeHashDelete(StoreEntry * e) { hash_remove_link(store_table, (hash_link *) e); +#if HEAP_REPLACEMENT + if (e->node) { + debug(20, 4) ("storeHashDelete: deleting node 0x%x\n", e->node); + heap_delete(store_heap, e->node); + e->node = NULL; + } +#else dlinkDelete(&e->lru, &store_list); +#endif storeKeyFree(e->key); e->key = NULL; } @@ -205,8 +232,17 @@ void storeLockObject(StoreEntry * e) { if (e->lock_count++ == 0) { +#if HEAP_REPLACEMENT + /* there is no reason to take any action here. + * Squid by default is moving locked objects to the end of the LRU + * list to keep them from getting bumped into by the replacement + * algorithm. We can't do that so we will just have to handle them. + */ + debug(20, 4) ("storeLockObject: just locked node 0x%x\n", e->node); +#else dlinkDelete(&e->lru, &store_list); dlinkAdd(e, &e->lru, &store_list); +#endif } debug(20, 3) ("storeLockObject: key '%s' count=%d\n", storeKeyText(e->key), (int) e->lock_count); @@ -251,14 +287,22 @@ storeUnlockObject(StoreEntry * e) } else { storePurgeMem(e); if (EBIT_TEST(e->flags, KEY_PRIVATE)) { +#if HEAP_REPLACEMENT + /* + * Squid/LRU is moving things around in the linked list in order + * to keep from bumping into them when purging from the LRU list. + */ + debug(20, 4) ("storeUnlockObject: purged private node 0x%x\n", e->node); +#else dlinkDelete(&e->lru, &store_list); dlinkAddTail(e, &e->lru, &store_list); +#endif } } return 0; } -/* Lookup an object in the cache. +/* Lookup an object in the cache. * return just a reference to object, don't start swapping in yet. */ StoreEntry * storeGet(const cache_key * key) @@ -324,6 +368,10 @@ storeSetPublicKey(StoreEntry * e) * If RELEASE_REQUEST is set, then ENTRY_CACHABLE should not * be set, and storeSetPublicKey() should not be called. */ +#if HEAP_REPLACEMENT + if (EBIT_TEST(e->flags, RELEASE_REQUEST)) + debug(20, 1) ("assertion failed: RELEASE key %s, url %s\n", e->key, mem->url); +#endif assert(!EBIT_TEST(e->flags, RELEASE_REQUEST)); newkey = storeKeyPublic(mem->url, mem->method); if ((e2 = (StoreEntry *) hash_lookup(store_table, newkey))) { @@ -506,10 +554,17 @@ storeCheckCachable(StoreEntry * e) } else if (fdNFree() < RESERVED_FD) { debug(20, 2) ("storeCheckCachable: NO: too many FD's open\n"); store_check_cachable_hist.no.too_many_open_fds++; +#if HEAP_REPLACEMENT + /* + * With the HEAP-based replacement policies a low reference age should not + * prevent cacheability of an object. We do not use LRU age at all. + */ +#else } else if (storeExpiredReferenceAge() < 300) { debug(20, 2) ("storeCheckCachable: NO: LRU Age = %d\n", storeExpiredReferenceAge()); store_check_cachable_hist.no.lru_age_too_low++; +#endif } else { store_check_cachable_hist.yes.Default++; return 1; @@ -580,7 +635,7 @@ storeComplete(StoreEntry * e) /* * Someone wants to abort this transfer. Set the reason in the * request structure, call the server-side callback and mark the - * entry for releasing + * entry for releasing */ void storeAbort(StoreEntry * e) @@ -629,8 +684,16 @@ storeGetMemSpace(int size) static time_t last_check = 0; int pages_needed; dlink_node *m; - dlink_node *head; dlink_node *prev = NULL; + int locked = 0; +#if !HEAP_REPLACEMENT + dlink_node *head; +#else + heap *heap = inmem_heap; + heap_key age, min_age = 0.0; + dlink_list locked_entries; + locked_entries.head = locked_entries.tail = NULL; +#endif if (squid_curtime == last_check) return; last_check = squid_curtime; @@ -640,6 +703,39 @@ storeGetMemSpace(int size) if (store_dirs_rebuilding) return; debug(20, 2) ("storeGetMemSpace: Starting, need %d pages\n", pages_needed); +#if HEAP_REPLACEMENT + while (heap_nodes(heap) > 0) { + age = heap_peepminkey(heap); + e = heap_extractmin(heap); + e->mem_obj->node = NULL; /* no longer in the heap */ + if (storeEntryLocked(e)) { + locked++; + debug(20, 5) ("storeGetMemSpace: locked key %s\n", storeKeyText(e->key)); + dlinkAdd(e, &e->lock_list, &locked_entries); + continue; + } + released++; + debug(20, 3) ("Released memory object with key %f size %d refs %d url %s\n", + age, e->swap_file_sz, e->refcount, e->mem_obj->url); + min_age = age; + storePurgeMem(e); + if (memInUse(MEM_STMEM_BUF) + pages_needed < store_pages_max) + break; + } + /* + * Increase the heap age factor. + */ + if (min_age > 0) + heap->age = min_age; + /* + * Reinsert all bumped locked entries back into heap... + */ + for (m = locked_entries.tail; m; m = prev) { + prev = m->prev; + e = m->data; + e->mem_obj->node = heap_insert(inmem_heap, e); + } +#else head = inmem_list.head; for (m = inmem_list.tail; m; m = prev) { if (m == head) @@ -647,6 +743,7 @@ storeGetMemSpace(int size) prev = m->prev; e = m->data; if (storeEntryLocked(e)) { + locked++; dlinkDelete(m, &inmem_list); dlinkAdd(e, m, &inmem_list); continue; @@ -656,6 +753,9 @@ storeGetMemSpace(int size) if (memInUse(MEM_STMEM_BUF) + pages_needed < store_pages_max) break; } +#endif + debug(20, 3) ("storeGetMemSpace: released %d/%d locked %d\n", + released, hot_obj_count, locked); debug(20, 3) ("storeGetMemSpace stats:\n"); debug(20, 3) (" %6d HOT objects\n", hot_obj_count); debug(20, 3) (" %6d were released\n", released); @@ -665,7 +765,7 @@ storeGetMemSpace(int size) #define MAINTAIN_MAX_SCAN 1024 #define MAINTAIN_MAX_REMOVE 64 -/* +/* * This routine is to be called by main loop in main.c. * It removes expired objects on only one bucket for each time called. * returns the number of objects removed @@ -685,6 +785,17 @@ storeMaintainSwapSpace(void *datanotused) int max_remove; double f; static time_t last_warn_time = 0; +#if HEAP_REPLACEMENT + heap *heap = store_heap; + heap_key age, min_age = 0.0; + dlink_list locked_entries; + locked_entries.head = locked_entries.tail = NULL; +#if HEAP_REPLACEMENT_DEBUG + if (!verify_heap_property(store_heap)) { + debug(20, 1) ("Heap property violated!\n"); + } +#endif +#endif /* We can't delete objects while rebuilding swap */ if (store_dirs_rebuilding) { eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0, 1); @@ -698,6 +809,64 @@ storeMaintainSwapSpace(void *datanotused) } debug(20, 3) ("storeMaintainSwapSpace: f=%f, max_scan=%d, max_remove=%d\n", f, max_scan, max_remove); +#if HEAP_REPLACEMENT + while (heap_nodes(heap) > 0) { + age = heap_peepminkey(heap); + e = heap_extractmin(heap); + e->node = NULL; /* no longer in the heap */ + scanned++; + if (storeEntryLocked(e)) { + /* + * Entry is in use ... put it in a linked list to ignore it. + */ + if (!EBIT_TEST(e->flags, ENTRY_SPECIAL)) { + /* + * If this was a "SPECIAL" do not add it back into the heap. + * It will always be "SPECIAL" and therefore never removed. + */ + debug(20, 4) ("storeMaintainSwapSpace: locked url %s\n", + (e->mem_obj && e->mem_obj->url) ? e->mem_obj->url : storeKeyText(e->key)); + dlinkAdd(e, &e->lock_list, &locked_entries); + } + locked++; + continue; + } else if (storeCheckExpired(e)) { + /* + * Note: This will not check the reference age ifdef HEAP_REPLACEMENT, + * but it does some other useful checks... + */ + expired++; + debug(20, 3) ("Released store object age %f size %d refs %d key %s\n", + age, e->swap_file_sz, e->refcount, storeKeyText(e->key)); + min_age = age; + storeRelease(e); + } else { + /* + * Did not expire the object so we need to add it back into the heap! + */ + debug(20, 5) ("storeMaintainSwapSpace: non-expired %s\n", storeKeyText(e->key)); + dlinkAdd(e, &e->lock_list, &locked_entries); + continue; + } + if ((store_swap_size < store_swap_low) + || (expired >= max_remove) + || (scanned >= max_scan)) + break; + } + /* + * Bump the heap age factor. + */ + if (min_age > 0) + heap->age = min_age; + /* + * Reinsert all bumped locked entries back into heap... + */ + for (m = locked_entries.tail; m; m = prev) { + prev = m->prev; + e = m->data; + e->node = heap_insert(store_heap, e); + } +#else for (m = store_list.tail; m; m = prev) { prev = m->prev; e = m->data; @@ -724,6 +893,9 @@ storeMaintainSwapSpace(void *datanotused) if (scanned >= max_scan) break; } +#endif + debug(20, (expired ? 2 : 3)) ("storeMaintainSwapSpace: scanned %d/%d removed %d/%d locked %d f=%.03f\n", + scanned, max_scan, expired, max_remove, locked, f); debug(20, 3) ("storeMaintainSwapSpace stats:\n"); debug(20, 3) (" %6d objects\n", memInUse(MEM_STOREENTRY)); debug(20, 3) (" %6d were scanned\n", scanned); @@ -884,6 +1056,72 @@ storeInitHashValues(void) debug(20, 1) ("Max Swap size: %d KB\n", Config.Swap.maxSize); } +#if HEAP_REPLACEMENT + +/* + * For a description of these cache replacement policies see -- + * http://www.hpl.hp.com/personal/John_Dilley/caching/wcw.html + */ + +/* + * Key generation function to implement the LFU-DA policy (Least + * Frequently Used with Dynamic Aging). Similar to classical LFU + * but with aging to handle turnover of the popular document set. + * Maximizes byte hit rate by keeping more currently popular objects + * in cache regardless of size. Achieves lower hit rate than GDS + * because there are more large objects in cache (so less room for + * smaller popular objects). + * + * This version implements a tie-breaker based upon recency + * (e->lastref): for objects that have the same reference count + * the most recent object wins (gets a higher key value). + */ +static heap_key +HeapKeyGen_StoreEntry_LFUDA(void *entry, double age) +{ + StoreEntry *e = entry; + double tie = (e->lastref > 1) ? (1.0 / e->lastref) : 1; + return age + e->refcount - tie; +} + + +/* + * Key generation function to implement the GDS-Frequency policy. + * Similar to Greedy Dual-Size Hits policy, but adds aging of + * documents to prevent pollution. Maximizes object hit rate by + * keeping more small, popular objects in cache. Achieves lower + * byte hit rate than LFUDA because there are fewer large objects + * in cache. + * + * This version implements a tie-breaker based upon recency + * (e->lastref): for objects that have the same reference count + * the most recent object wins (gets a higher key value). + */ +static heap_key +HeapKeyGen_StoreEntry_GDSF(void *entry, double age) +{ + StoreEntry *e = entry; + double size = e->swap_file_sz ? e->swap_file_sz : 1.0; + double tie = (e->lastref > 1) ? (1.0 / e->lastref) : 1; + return age + ((double) e->refcount / size) - tie; +} + +/* + * Key generation function to implement the LRU policy. Normally + * one would not do this with a heap -- use the linked list instead. + * For testing and performance characterization it was useful. + * Don't use it unless you are trying to compare performance among + * heap-based replacement policies... + */ +static heap_key +HeapKeyGen_StoreEntry_LRU(void *entry, double age) +{ + StoreEntry *e = entry; + return (heap_key) e->lastref; +} + +#endif + void storeInit(void) { @@ -893,8 +1131,39 @@ storeInit(void) store_hash_buckets, storeKeyHashHash); storeDigestInit(); storeLogOpen(); +#if HEAP_REPLACEMENT + /* + * Create new heaps with cache replacement policies attached to them. + * The cache replacement policy is specified as either GDSF or LFUDA in + * the squid.conf configuration file. Note that the replacement policy + * applies only to the disk replacement algorithm. Memory replacement + * always uses GDSF since we want to maximize object hit rate. + */ + inmem_heap = new_heap(1000, HeapKeyGen_StoreEntry_GDSF); + if (Config.replPolicy) { + if (tolower(Config.replPolicy[0]) == 'g') { + debug(20, 1) ("Using GDSF disk replacement policy\n"); + store_heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF); + } else if (tolower(Config.replPolicy[0]) == 'l') { + if (tolower(Config.replPolicy[1]) == 'f') { + debug(20, 1) ("Using LFUDA disk replacement policy\n"); + store_heap = new_heap(10000, HeapKeyGen_StoreEntry_LFUDA); + } else if (tolower(Config.replPolicy[1]) == 'r') { + debug(20, 1) ("Using LRU heap disk replacement policy\n"); + store_heap = new_heap(10000, HeapKeyGen_StoreEntry_LRU); + } + } else { + debug(20, 1) ("Unrecognized replacement_policy; using GDSF\n"); + store_heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF); + } + } else { + debug(20, 1) ("Using default disk replacement policy (GDSF)\n"); + store_heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF); + } +#else store_list.head = store_list.tail = NULL; inmem_list.head = inmem_list.tail = NULL; +#endif stackInit(&LateReleaseStack); eventAdd("storeLateRelease", storeLateRelease, NULL, 1.0, 1); storeDirInit(); @@ -938,12 +1207,25 @@ storeCheckExpired(const StoreEntry * e) return 1; if (EBIT_TEST(e->flags, ENTRY_NEGCACHED) && squid_curtime >= e->expires) return 1; +#if HEAP_REPLACEMENT + /* + * With HEAP_REPLACEMENT we are not using the LRU reference age, the heap + * controls the replacement of objects. + */ + return 1; +#else if (squid_curtime - e->lastref > storeExpiredReferenceAge()) return 1; return 0; +#endif } -/* +#if HEAP_REPLACEMENT +/* + * The non-LRU cache replacement policies do not use LRU referenceAge + */ +#else +/* * storeExpiredReferenceAge * * The LRU age is scaled exponentially between 1 minute and @@ -970,6 +1252,7 @@ storeExpiredReferenceAge(void) age = 31536000; return age; } +#endif void storeNegativeCache(StoreEntry * e) @@ -1103,10 +1386,33 @@ storeSetMemStatus(StoreEntry * e, int new_status) assert(mem != NULL); if (new_status == IN_MEMORY) { assert(mem->inmem_lo == 0); +#if HEAP_REPLACEMENT + if (mem->node == NULL) { + if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) { + debug(20, 4) ("storeSetMemStatus: not inserting special %s\n", mem->url); + } else { + mem->node = heap_insert(inmem_heap, e); + debug(20, 4) ("storeSetMemStatus: inserted mem node 0x%x\n", mem->node); + } + } +#else dlinkAdd(e, &mem->lru, &inmem_list); +#endif hot_obj_count++; } else { +#if HEAP_REPLACEMENT + /* + * It's being removed from the memory heap; is it already gone? + */ + if (mem->node) { + heap_delete(inmem_heap, mem->node); + debug(20, 4) ("storeSetMemStatus: deleted mem node 0x%x\n", + mem->node); + mem->node = NULL; + } +#else dlinkDelete(&mem->lru, &inmem_list); +#endif hot_obj_count--; } e->mem_status = new_status; @@ -1183,3 +1489,13 @@ storeEntryReset(StoreEntry * e) httpReplyDestroy(mem->reply); mem->reply = httpReplyCreate(); } + +void +storeHeapPositionUpdate(StoreEntry * e) +{ + if (e->node) + heap_update(store_heap, e->node, e); + assert(e->mem_obj); + if (e->mem_obj->node) + heap_update(inmem_heap, e->mem_obj->node, e); +} diff --git a/src/store_dir.cc b/src/store_dir.cc index 2f8bb057f5..c644c67b79 100644 --- a/src/store_dir.cc +++ b/src/store_dir.cc @@ -1,6 +1,6 @@ /* - * $Id: store_dir.cc,v 1.97 1999/05/26 06:48:08 wessels Exp $ + * $Id: store_dir.cc,v 1.98 1999/06/24 20:20:14 wessels Exp $ * * DEBUG: section 47 Store Directory Routines * AUTHOR: Duane Wessels @@ -380,7 +380,11 @@ storeDirWriteCleanLogs(int reopen) SwapDir *sd; int dirn; int N = Config.cacheSwap.n_configured; +#if HEAP_REPLACEMENT + int node; +#else dlink_node *m; +#endif if (store_dirs_rebuilding) { debug(20, 1) ("Not currently OK to rewrite swap log.\n"); debug(20, 1) ("storeDirWriteCleanLogs: Operation aborted.\n"); @@ -396,8 +400,17 @@ storeDirWriteCleanLogs(int reopen) continue; } } - for (m = store_list.tail; m; m = m->prev) { +#if HEAP_REPLACEMENT + for (node = 0; node < heap_nodes(store_heap); node++) +#else + for (m = store_list.tail; m; m = m->prev) +#endif + { +#if HEAP_REPLACEMENT + e = (StoreEntry *) heap_peep(store_heap, node); +#else e = m->data; +#endif if (e->swap_file_number < 0) continue; if (e->swap_status != SWAPOUT_DONE) diff --git a/src/store_swapout.cc b/src/store_swapout.cc index 1b5a8e525c..098e0dd0c3 100644 --- a/src/store_swapout.cc +++ b/src/store_swapout.cc @@ -1,6 +1,6 @@ /* - * $Id: store_swapout.cc,v 1.53 1999/05/26 05:01:05 wessels Exp $ + * $Id: store_swapout.cc,v 1.54 1999/06/24 20:20:17 wessels Exp $ * * DEBUG: section 20 Storage Manager Swapout Functions * AUTHOR: Duane Wessels @@ -237,6 +237,9 @@ storeSwapOutFileClosed(void *data, int errflag, storeIOState * sio) debug(20, 3) ("storeSwapOutFileClosed: SwapOut complete: '%s' to %08X\n", storeUrl(e), e->swap_file_number); e->swap_file_sz = objectLen(e) + mem->swap_hdr_sz; +#if HEAP_REPLACEMENT + storeHeapPositionUpdate(e); +#endif e->swap_status = SWAPOUT_DONE; storeDirUpdateSwapSize(e->swap_file_number, e->swap_file_sz, 1); if (storeCheckCachable(e)) { diff --git a/src/structs.h b/src/structs.h index 9bf37a562a..ab2627ebf9 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1,6 +1,6 @@ /* - * $Id: structs.h,v 1.299 1999/06/16 22:10:43 wessels Exp $ + * $Id: structs.h,v 1.300 1999/06/24 20:20:19 wessels Exp $ * * * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ @@ -231,6 +231,14 @@ struct _SquidConfig { int pct; size_t max; } quickAbort; +#if HEAP_REPLACEMENT + char *replPolicy; +#else + /* + * Note: the non-LRU policies do not use referenceAge, but we cannot + * remove it until we find out how to implement #else for cf_parser.c + */ +#endif time_t referenceAge; time_t negativeTtl; time_t negativeDnsTtl; @@ -1233,7 +1241,14 @@ struct _MemObject { void *data; } abort; char *log_url; +#if HEAP_REPLACEMENT + /* + * A MemObject knows where it is in the in-memory heap. + */ + heap_node *node; +#else dlink_node lru; +#endif int id; ssize_t object_sz; size_t swap_hdr_sz; @@ -1252,7 +1267,12 @@ struct _StoreEntry { u_short refcount; u_short flags; sfileno swap_file_number; +#if HEAP_REPLACEMENT + heap_node *node; + dlink_node lock_list; +#else dlink_node lru; +#endif u_short lock_count; /* Assume < 65536! */ mem_status_t mem_status:3; ping_status_t ping_status:3; diff --git a/src/tools.cc b/src/tools.cc index e25935fb88..999d65c5b2 100644 --- a/src/tools.cc +++ b/src/tools.cc @@ -1,6 +1,6 @@ /* - * $Id: tools.cc,v 1.182 1999/05/27 03:21:42 wessels Exp $ + * $Id: tools.cc,v 1.183 1999/06/24 20:20:21 wessels Exp $ * * DEBUG: section 21 Misc Functions * AUTHOR: Harvest Derived @@ -213,7 +213,10 @@ PrintRusage(void) { struct rusage rusage; squid_getrusage(&rusage); - fprintf(debug_log, "CPU Usage: %.3f seconds\n", rusage_cputime(&rusage)); + fprintf(debug_log, "CPU Usage: %.3f seconds = %.3f user + %.3f sys\n", + rusage_cputime(&rusage), + rusage.ru_utime.tv_sec + ((double) rusage.ru_utime.tv_usec / 1000000.0), + rusage.ru_stime.tv_sec + ((double) rusage.ru_stime.tv_usec / 1000000.0)); fprintf(debug_log, "Maximum Resident Size: %d KB\n", rusage_maxrss(&rusage)); fprintf(debug_log, "Page faults with physical i/o: %d\n",