]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Optimization: Fewer memory (re)allocations for HTTP headers (#239) M-staged-PR239
authorEduard Bagdasaryan <eduard.bagdasaryan@measurement-factory.com>
Fri, 13 Jul 2018 21:27:19 +0000 (21:27 +0000)
committerSquid Anubis <squid-anubis@squid-cache.org>
Sat, 14 Jul 2018 17:33:11 +0000 (17:33 +0000)
Tests revealed multiple fresh memory allocations/deallocations while
storing small (few fields) HTTP headers. Many popular sites use larger
headers (15-30 fields). To avoid expensive memory operations:

1. Pool all std::vector<HttpHeaderEntries*> memory allocations.
2. Prevent reallocations (for HTTP headers with fewer than 32 fields).

This optimization deals with storing the header index. It does not
affect how individual header fields are stored.

src/HttpHeader.cc
src/HttpHeader.h
src/mem/Makefile.am
src/mem/PoolingAllocator.h [new file with mode: 0644]
src/mem/forward.h
src/mem/old_api.cc
src/tests/stub_libmem.cc

index a264c7c37fd03d429a162c0c357752c0c4d0ce7f..185399fafc3a44548597fad6eddb5ffd3670a7be 100644 (file)
@@ -145,6 +145,7 @@ httpHeaderInitModule(void)
 
 HttpHeader::HttpHeader() : owner (hoNone), len (0), conflictingContentLength_(false)
 {
+    entries.reserve(32);
     httpHeaderMaskInit(&mask, 0);
 }
 
@@ -152,11 +153,13 @@ HttpHeader::HttpHeader(const http_hdr_owner_type anOwner): owner(anOwner), len(0
 {
     assert(anOwner > hoNone && anOwner < hoEnd);
     debugs(55, 7, "init-ing hdr: " << this << " owner: " << owner);
+    entries.reserve(32);
     httpHeaderMaskInit(&mask, 0);
 }
 
 HttpHeader::HttpHeader(const HttpHeader &other): owner(other.owner), len(other.len), conflictingContentLength_(false)
 {
+    entries.reserve(other.entries.capacity());
     httpHeaderMaskInit(&mask, 0);
     update(&other); // will update the mask as well
 }
index 64b0651c5ff6e31e13f80380c937f9d2bcc76e08..cc21d3e0c151dea05b148d885ad1a64fa14331b6 100644 (file)
@@ -14,7 +14,7 @@
 #include "http/RegisteredHeaders.h"
 /* because we pass a spec by value */
 #include "HttpHeaderMask.h"
-#include "mem/forward.h"
+#include "mem/PoolingAllocator.h"
 #include "sbuf/forward.h"
 #include "SquidString.h"
 
@@ -153,7 +153,7 @@ public:
     inline bool chunked() const; ///< whether message uses chunked Transfer-Encoding
 
     /* protected, do not use these, use interface functions instead */
-    std::vector<HttpHeaderEntry *> entries;     /**< parsed fields in raw format */
+    std::vector<HttpHeaderEntry*, PoolingAllocator<HttpHeaderEntry*> > entries; /**< parsed fields in raw format */
     HttpHeaderMask mask;    /**< bit set <=> entry present */
     http_hdr_owner_type owner;  /**< request or reply */
     int len;            /**< length when packed, not counting terminating null-byte */
index f940cf8fcd2d1724385d8408e77a09355e8ff638..4b4f7057b73b83e7323e40eb2f5d87371a255329 100644 (file)
@@ -14,11 +14,12 @@ libmem_la_SOURCES = \
        AllocatorProxy.cc \
        AllocatorProxy.h \
        forward.h \
-       old_api.cc \
        Meter.h \
+       old_api.cc \
        Pool.cc \
        Pool.h \
        PoolChunked.cc \
        PoolChunked.h \
+       PoolingAllocator.h \
        PoolMalloc.cc \
        PoolMalloc.h
diff --git a/src/mem/PoolingAllocator.h b/src/mem/PoolingAllocator.h
new file mode 100644 (file)
index 0000000..c6ec004
--- /dev/null
@@ -0,0 +1,42 @@
+/*
++ * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
++ *
++ * Squid software is distributed under GPLv2+ license and includes
++ * contributions from numerous individuals and organizations.
++ * Please see the COPYING and CONTRIBUTORS files for details.
++ */
+
+#ifndef SQUID_MEM_POOLINGALLOCATOR_H
+#define SQUID_MEM_POOLINGALLOCATOR_H
+
+#include "mem/forward.h"
+
+/// STL Allocator that uses Squid memory pools for memory management
+template <class Value>
+class PoolingAllocator
+{
+public:
+    /* STL Allocator API */
+    using value_type = Value;
+    PoolingAllocator() noexcept {}
+    template <class Other> PoolingAllocator(const PoolingAllocator<Other> &) noexcept {}
+    value_type *allocate(std::size_t n) { return static_cast<value_type*>(memAllocRigid(n*sizeof(value_type))); }
+    void deallocate(value_type *vp, std::size_t n) noexcept { memFreeRigid(vp, n*sizeof(value_type)); }
+};
+
+template <class L, class R>
+inline bool
+operator ==(const PoolingAllocator<L>&, const PoolingAllocator<R>&) noexcept
+{
+    return true;
+}
+
+template <class L, class R>
+inline bool
+operator !=(const PoolingAllocator<L> &l, const PoolingAllocator<R> &r) noexcept
+{
+    return !(l == r);
+}
+
+#endif /* SQUID_MEM_POOLINGALLOCATOR_H */
+
index e1bd8fcd4f5a02ec4106bfe20ab285259955cf77..96c14a4a66c947d7b9882dcf287a20a2f6a753d3 100644 (file)
@@ -59,11 +59,13 @@ void memConfigure(void);
 void *memAllocate(mem_type);
 void *memAllocString(size_t net_size, size_t * gross_size);
 void *memAllocBuf(size_t net_size, size_t * gross_size);
+void *memAllocRigid(size_t net_size);
 void *memReallocBuf(void *buf, size_t net_size, size_t * gross_size);
 /// Free a element allocated by memAllocate()
 void memFree(void *, int type);
 void memFreeString(size_t size, void *);
 void memFreeBuf(size_t size, void *);
+void memFreeRigid(void *, size_t net_size);
 FREE *memFreeBufFunc(size_t size);
 int memInUse(mem_type);
 void memDataInit(mem_type, const char *, size_t, int, bool doZero = true);
index 5a260d6d9c70e247a1b47dfc96483351afca3878..e360aa6e039773b34657d17ce6f09ce8d306e1d4 100644 (file)
@@ -245,6 +245,22 @@ memAllocString(size_t net_size, size_t * gross_size)
     return xcalloc(1, net_size);
 }
 
+void *
+memAllocRigid(size_t net_size)
+{
+    // TODO: Use memAllocString() instead (after it stops zeroing memory).
+
+    if (const auto pool = memFindStringPool(net_size, true)) {
+        ++StrCountMeter;
+        StrVolumeMeter += pool->objectSize();
+        return pool->alloc();
+    }
+
+    ++StrCountMeter;
+    StrVolumeMeter += net_size;
+    return xmalloc(net_size);
+}
+
 size_t
 memStringCount()
 {
@@ -271,6 +287,23 @@ memFreeString(size_t size, void *buf)
     StrVolumeMeter -= size;
 }
 
+void
+memFreeRigid(void *buf, size_t net_size)
+{
+    // TODO: Use memFreeString() instead (after removing fuzzy=false pool search).
+
+    if (const auto pool = memFindStringPool(net_size, true)) {
+        pool->freeOne(buf);
+        StrVolumeMeter -= pool->objectSize();
+        --StrCountMeter;
+        return;
+    }
+
+    xfree(buf);
+    StrVolumeMeter -= net_size;
+    --StrCountMeter;
+}
+
 /* Find the best fit MEM_X_BUF type */
 static mem_type
 memFindBufSizeType(size_t net_size, size_t * gross_size)
index 474a4ae1d8a8b3b029cc094912cb80acf231c12c..ac0b6186cfde5836da15a8e2692131c647d02ccc 100644 (file)
@@ -40,6 +40,11 @@ void * memAllocate(mem_type)
 
 void *memAllocString(size_t net_size, size_t * gross_size) {return memAllocBuf(net_size, gross_size);}
 
+void *memAllocRigid(size_t net_size)
+{
+    return xmalloc(net_size);
+}
+
 void *
 memAllocBuf(size_t net_size, size_t * gross_size)
 {
@@ -62,6 +67,7 @@ memReallocBuf(void *oldbuf, size_t net_size, size_t * gross_size)
 
 void memFree(void *p, int) {xfree(p);}
 void memFreeString(size_t, void *buf) {xfree(buf);}
+void memFreeRigid(void *buf, size_t) {xfree(buf);}
 void memFreeBuf(size_t, void *buf) {xfree(buf);}
 static void cxx_xfree(void * ptr) {xfree(ptr);}
 FREE *memFreeBufFunc(size_t) {return cxx_xfree;}