]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
committing Dilley's HEAP replacement code
authorwessels <>
Fri, 25 Jun 1999 02:19:53 +0000 (02:19 +0000)
committerwessels <>
Fri, 25 Jun 1999 02:19:53 +0000 (02:19 +0000)
14 files changed:
CONTRIBUTORS
lib/Makefile.in
src/cache_cf.cc
src/cf.data.pre
src/client_side.cc
src/globals.h
src/protos.h
src/squid.h
src/stat.cc
src/store.cc
src/store_dir.cc
src/store_swapout.cc
src/structs.h
src/tools.cc

index cdea6e9566d023cc9e91d621c41fe89cb97c1049..30cb715cbe26eae335fddb9dd6f2734e32b36cbd 100644 (file)
@@ -69,6 +69,7 @@ and ideas to make this software available.
        Michael Lupp <mike@nemesis.saar.de>
        Niall Doherty <ndoherty@eei.ericsson.se>
        Pedro Ribeiro <pribeiro@isel.pt>
+       John Dilley <jad@hpl.hp.com>
 
 Development of this caching software is funded by the National Science
 Foundation (grants NCR-9616602 and NCR-9521745).  Paid staff members on
index 2645c5947a309586ff19627fbf8be4ad686bc58c..bd42b6234dc28552dcb24a1b354739c99f120221 100644 (file)
@@ -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
index a36f0b1b41802adc3388523e0de66b5b31e80eaf..850943404c5cd581c5157a37119bd20b64a2e789 100644 (file)
@@ -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);
index 9afafce6ad11fba0f8d831595a6fdeac0f4c2d11..fc5cc0722e114592d706c175a0c8d6de90af2666 100644 (file)
@@ -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
 
index 6acd06a4b05ea42643d93e464d0e540202f4eb5e..dfc4dcc514900cd7855d9f4cab8969826d122260 100644 (file)
@@ -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);
index 0a897a1dab0c603445aa17b599d750a84230cb9c..efd02a816df8a97d574369d3d80f2334e3135227 100644 (file)
@@ -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 */
index afe2a881c178ed78253163ec11318c0028433d2e..dc8a9abc37406a8ddf32ca898ad9d8a61575484c 100644 (file)
@@ -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;
index 8b734d85fd81c74a987a81869a75729883f34aba..fb8727b9ad5480103b13fd4f14397bf278986509 100644 (file)
@@ -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"
index d269179da5e76c86e3ce9919d3f1c09251cffe78..1ac30ecff474b0eee2cb31554e4b5e51e7768fdb 100644 (file)
@@ -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",
index 12dffddce9530d33e0d0cd72ad4efd8e04688be4..2dc30a8c6cb0c9a898479c96fccd7a65c8a022d3 100644 (file)
@@ -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
  *  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);
+}
index 2f8bb057f5fbef4729c416c39cabaa19a2d34a81..c644c67b792b8f8942317fbcc42d21bc25c463ee 100644 (file)
@@ -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)
index 1b5a8e525c087c793634510dd2684edc180427bd..098e0dd0c34055c6c572ee0c88f6f91a37900d22 100644 (file)
@@ -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)) {
index 9bf37a562ab4e42d869930b8e52225a8fdf6c314..ab2627ebf97fa602552034ab526152e14f83dff0 100644 (file)
@@ -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;
index e25935fb883960fe20fec5af4ce32e0eb50a0934..999d65c5b2c4b9e3592b406e7dc5b532b1c9248b 100644 (file)
@@ -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",