| /** @file |
| |
| A brief file description |
| |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| #include "P_Cache.h" |
| |
| struct RamCacheLRUEntry { |
| INK_MD5 key; |
| uint32_t auxkey1; |
| uint32_t auxkey2; |
| LINK(RamCacheLRUEntry, lru_link); |
| LINK(RamCacheLRUEntry, hash_link); |
| Ptr<IOBufferData> data; |
| }; |
| |
| struct RamCacheLRU: public RamCache { |
| int64_t max_bytes; |
| int64_t bytes; |
| int64_t objects; |
| |
| // returns 1 on found/stored, 0 on not found/stored, if provided auxkey1 and auxkey2 must match |
| int get(INK_MD5 *key, Ptr<IOBufferData> *ret_data, uint32_t auxkey1 = 0, uint32_t auxkey2 = 0); |
| int put(INK_MD5 *key, IOBufferData *data, uint32_t len, bool copy = false, uint32_t auxkey1 = 0, uint32_t auxkey2 = 0); |
| int fixup(INK_MD5 *key, uint32_t old_auxkey1, uint32_t old_auxkey2, uint32_t new_auxkey1, uint32_t new_auxkey2); |
| |
| void init(int64_t max_bytes, Vol *vol); |
| |
| // private |
| uint16_t *seen; |
| Que(RamCacheLRUEntry, lru_link) lru; |
| DList(RamCacheLRUEntry, hash_link) *bucket; |
| int nbuckets; |
| int ibuckets; |
| Vol *vol; |
| |
| void resize_hashtable(); |
| RamCacheLRUEntry *remove(RamCacheLRUEntry *e); |
| |
| RamCacheLRU():bytes(0), objects(0), seen(0), bucket(0), nbuckets(0), ibuckets(0), vol(NULL) {} |
| }; |
| |
| ClassAllocator<RamCacheLRUEntry> ramCacheLRUEntryAllocator("RamCacheLRUEntry"); |
| |
| static const int bucket_sizes[] = { |
| 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139, |
| 524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, 67108859, |
| 134217689, 268435399, 536870909 |
| }; |
| |
| void RamCacheLRU::resize_hashtable() { |
| int anbuckets = bucket_sizes[ibuckets]; |
| DDebug("ram_cache", "resize hashtable %d", anbuckets); |
| int64_t s = anbuckets * sizeof(DList(RamCacheLRUEntry, hash_link)); |
| DList(RamCacheLRUEntry, hash_link) *new_bucket = (DList(RamCacheLRUEntry, hash_link) *)ats_malloc(s); |
| memset(new_bucket, 0, s); |
| if (bucket) { |
| for (int64_t i = 0; i < nbuckets; i++) { |
| RamCacheLRUEntry *e = 0; |
| while ((e = bucket[i].pop())) |
| new_bucket[e->key.word(3) % anbuckets].push(e); |
| } |
| ats_free(bucket); |
| } |
| bucket = new_bucket; |
| nbuckets = anbuckets; |
| ats_free(seen); |
| int size = bucket_sizes[ibuckets] * sizeof(uint16_t); |
| if (cache_config_ram_cache_use_seen_filter) { |
| seen = (uint16_t*)ats_malloc(size); |
| memset(seen, 0, size); |
| } |
| } |
| |
| void |
| RamCacheLRU::init(int64_t abytes, Vol *avol) { |
| vol = avol; |
| max_bytes = abytes; |
| DDebug("ram_cache", "initializing ram_cache %" PRId64 " bytes", abytes); |
| if (!max_bytes) |
| return; |
| resize_hashtable(); |
| } |
| |
| int |
| RamCacheLRU::get(INK_MD5 * key, Ptr<IOBufferData> *ret_data, uint32_t auxkey1, uint32_t auxkey2) { |
| if (!max_bytes) |
| return 0; |
| uint32_t i = key->word(3) % nbuckets; |
| RamCacheLRUEntry *e = bucket[i].head; |
| while (e) { |
| if (e->key == *key && e->auxkey1 == auxkey1 && e->auxkey2 == auxkey2) { |
| lru.remove(e); |
| lru.enqueue(e); |
| (*ret_data) = e->data; |
| DDebug("ram_cache", "get %X %d %d HIT", key->word(3), auxkey1, auxkey2); |
| CACHE_SUM_DYN_STAT_THREAD(cache_ram_cache_hits_stat, 1); |
| return 1; |
| } |
| e = e->hash_link.next; |
| } |
| DDebug("ram_cache", "get %X %d %d MISS", key->word(3), auxkey1, auxkey2); |
| CACHE_SUM_DYN_STAT_THREAD(cache_ram_cache_misses_stat, 1); |
| return 0; |
| } |
| |
| RamCacheLRUEntry * RamCacheLRU::remove(RamCacheLRUEntry *e) { |
| RamCacheLRUEntry *ret = e->hash_link.next; |
| uint32_t b = e->key.word(3) % nbuckets; |
| bucket[b].remove(e); |
| lru.remove(e); |
| bytes -= e->data->block_size(); |
| CACHE_SUM_DYN_STAT_THREAD(cache_ram_cache_bytes_stat, -e->data->block_size()); |
| DDebug("ram_cache", "put %X %d %d FREED", e->key.word(3), e->auxkey1, e->auxkey2); |
| e->data = NULL; |
| THREAD_FREE(e, ramCacheLRUEntryAllocator, this_thread()); |
| objects--; |
| return ret; |
| } |
| |
| // ignore 'copy' since we don't touch the data |
| int RamCacheLRU::put(INK_MD5 *key, IOBufferData *data, uint32_t len, bool, uint32_t auxkey1, uint32_t auxkey2) { |
| if (!max_bytes) |
| return 0; |
| uint32_t i = key->word(3) % nbuckets; |
| if (cache_config_ram_cache_use_seen_filter) { |
| uint16_t k = key->word(3) >> 16; |
| uint16_t kk = seen[i]; |
| seen[i] = k; |
| if ((kk != (uint16_t)k)) { |
| DDebug("ram_cache", "put %X %d %d len %d UNSEEN", key->word(3), auxkey1, auxkey2, len); |
| return 0; |
| } |
| } |
| RamCacheLRUEntry *e = bucket[i].head; |
| while (e) { |
| if (e->key == *key) { |
| if (e->auxkey1 == auxkey1 && e->auxkey2 == auxkey2) { |
| lru.remove(e); |
| lru.enqueue(e); |
| return 1; |
| } else { // discard when aux keys conflict |
| e = remove(e); |
| continue; |
| } |
| } |
| e = e->hash_link.next; |
| } |
| e = THREAD_ALLOC(ramCacheLRUEntryAllocator, this_ethread()); |
| e->key = *key; |
| e->auxkey1 = auxkey1; |
| e->auxkey2 = auxkey2; |
| e->data = data; |
| bucket[i].push(e); |
| lru.enqueue(e); |
| bytes += data->block_size(); |
| objects++; |
| CACHE_SUM_DYN_STAT_THREAD(cache_ram_cache_bytes_stat, data->block_size()); |
| while (bytes > max_bytes) { |
| RamCacheLRUEntry *ee = lru.dequeue(); |
| if (ee) |
| remove(ee); |
| else |
| break; |
| } |
| DDebug("ram_cache", "put %X %d %d INSERTED", key->word(3), auxkey1, auxkey2); |
| if (objects > nbuckets) { |
| ++ibuckets; |
| resize_hashtable(); |
| } |
| return 1; |
| } |
| |
| int RamCacheLRU::fixup(INK_MD5 * key, uint32_t old_auxkey1, uint32_t old_auxkey2, uint32_t new_auxkey1, uint32_t new_auxkey2) { |
| if (!max_bytes) |
| return 0; |
| uint32_t i = key->word(3) % nbuckets; |
| RamCacheLRUEntry *e = bucket[i].head; |
| while (e) { |
| if (e->key == *key && e->auxkey1 == old_auxkey1 && e->auxkey2 == old_auxkey2) { |
| e->auxkey1 = new_auxkey1; |
| e->auxkey2 = new_auxkey2; |
| return 1; |
| } |
| e = e->hash_link.next; |
| } |
| return 0; |
| } |
| |
| RamCache *new_RamCacheLRU() { |
| return new RamCacheLRU; |
| } |