Skip to content

Commit 466c5de

Browse files
Imrove memory usage during huffman decoding
- Do not allocate new memory during hpack decoding if some memory is already available. (It can be available because we allocate 150 % memory for each header). - Move BUFFER_GET macros to hpack.c - Some codestyle fixes according checkpatch.pl
1 parent 05ca2f2 commit 466c5de

File tree

3 files changed

+100
-58
lines changed

3 files changed

+100
-58
lines changed

fw/hpack.c

Lines changed: 92 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,41 @@
3030

3131
#include "hpack_tbl.h"
3232

33+
/**
34+
* There are to cases when we allocate space for header:
35+
* - it->rspace is equal to zero. In this case we allocate
36+
* `len` bytes;
37+
* - it->rspace is not equal to zero. This means that we
38+
* already have buffer with rspace bytes available.
39+
* If this buffer is rather big to contains the whole
40+
* header or it is greater then sizeof(TfwStr) we use
41+
* it. (Save delta = len - it->rspace bytes. Later when
42+
* `it->rspace` will be exceeded we use it to allocate
43+
* new chunk).
44+
* Otherwise allocate new buffer (if we have some free
45+
* bytes in old buffer, but is is not enough for the
46+
* new header and is less then sizeof(TfwStr)), we allocate
47+
* new buffer to prevent extra header chunking.
48+
*/
49+
#define BUFFER_GET(len, it) \
50+
do { \
51+
\
52+
\
53+
BUG_ON(!(len)); \
54+
if (!(it)->rspace || \
55+
(len > (it)->rspace && (it)->rspace < sizeof(TfwStr))) \
56+
{ \
57+
(it)->rspace = len; \
58+
(it)->pos = tfw_pool_alloc_not_align((it)->pool, len); \
59+
T_DBG3("%s: get buffer, len=%lu, it->pos=[%p]," \
60+
" it->pos=%lu\n", __func__, (unsigned long)len, \
61+
(it)->pos, (unsigned long)(it)->pos); \
62+
} else { \
63+
(it)->to_alloc = len > (it)->rspace ? \
64+
len - (it)->rspace : 0; \
65+
} \
66+
} while (0)
67+
3368
#define HP_HDR_NAME(name) \
3469
(&(TfwStr){ \
3570
.chunks = &(TfwStr){ \
@@ -278,25 +313,32 @@ do { \
278313
last - src); \
279314
} while (0)
280315

281-
#define BUFFER_HDR_INIT(length, it) \
316+
#define BUFFER_HDR_INIT(it) \
282317
do { \
283318
(it)->hdr.data = (it)->pos; \
284-
(it)->hdr.len = length; \
319+
(it)->hdr.len = 0; \
285320
(it)->next = 0; \
286321
} while (0)
287322

288323
#define BUFFER_NAME_OPEN(length) \
289324
do { \
290325
WARN_ON_ONCE(!TFW_STR_EMPTY(&it->hdr)); \
291326
if (state & HPACK_FLAGS_HUFFMAN_NAME) { \
327+
/* \
328+
* Allocate extra 50% bytes. Huffman usually \
329+
* gives compression benefit about 20 - 60 %. \
330+
* Since we use extra allocated bytes for the \
331+
* next header, we can allocate extra 50% bytes \
332+
* without fear of losing a lot of memory. \
333+
*/ \
292334
unsigned long len = length + (length >> 1); \
293335
\
294336
BUFFER_GET(len, it); \
295337
if (!it->pos) { \
296338
r = -ENOMEM; \
297339
goto out; \
298340
} \
299-
BUFFER_HDR_INIT(len, it); \
341+
BUFFER_HDR_INIT(it); \
300342
} \
301343
} while (0)
302344

@@ -308,6 +350,13 @@ do { \
308350
? it->parsed_hdr->nchunks \
309351
: 1; \
310352
if (state & HPACK_FLAGS_HUFFMAN_VALUE) { \
353+
/* \
354+
* Allocate extra 50% bytes. Huffman usually \
355+
* gives compression benefit about 20 - 60 %. \
356+
* Since we use extra allocated bytes for the \
357+
* next header, we can allocate extra 50% bytes \
358+
* without fear of losing a lot of memory. \
359+
*/ \
311360
unsigned long len = length + (length >> 1); \
312361
\
313362
BUFFER_GET(len, it); \
@@ -316,13 +365,12 @@ do { \
316365
goto out; \
317366
} \
318367
if (!TFW_STR_EMPTY(&it->hdr)) { \
319-
r = tfw_hpack_exp_hdr(req->pool, len, \
320-
it); \
368+
r = tfw_hpack_exp_hdr(req->pool, 0, it); \
321369
if (unlikely(r)) \
322370
return r; \
323371
it->next = it->hdr.nchunks - 1; \
324372
} else { \
325-
BUFFER_HDR_INIT(length, it); \
373+
BUFFER_HDR_INIT(it); \
326374
} \
327375
} \
328376
} while (0)
@@ -460,41 +508,61 @@ static inline int
460508
tfw_hpack_huffman_write(char sym, TfwHttpReq *__restrict req)
461509
{
462510
TfwMsgParseIter *it = &req->pit;
511+
unsigned long to_alloc;
463512
bool np;
464513
int r;
465514

515+
#define ADJUST_HDR_LEN(it) \
516+
do { \
517+
TfwStr *hdr = &it->hdr; \
518+
TfwStr *last = TFW_STR_LAST(hdr); \
519+
\
520+
T_DBG3("%s: add to hdr, hdr->len=%lu, last->len=%lu," \
521+
" last->data=%.*s\n", __func__, hdr->len, last->len, \
522+
(int)last->len, last->data); \
523+
\
524+
++hdr->len; \
525+
if (!TFW_STR_PLAIN(hdr)) \
526+
++last->len; \
527+
} while (0)
528+
466529
if (it->rspace) {
467530
--it->rspace;
468531
*it->pos++ = sym;
532+
ADJUST_HDR_LEN(it);
469533
return 0;
470534
}
471535

472-
if (!(it->pos = tfw_pool_alloc_not_align_np(it->pool, 1, &np)))
473-
return -ENOMEM;
536+
to_alloc = it->to_alloc ? it->to_alloc : 1;
537+
if (to_alloc > 1)
538+
it->rspace = to_alloc - 1;
474539

475-
*it->pos = sym;
540+
it->pos = tfw_pool_alloc_not_align_np(it->pool, to_alloc, &np);
541+
if (!it->pos)
542+
return -ENOMEM;
476543

477544
T_DBG3("%s: it->rspace=%lu, sym=%c, np=%d\n", __func__,
478545
it->rspace, sym, np);
479546

480-
if (!np) {
481-
TfwStr *hdr = &it->hdr;
482-
TfwStr *last = TFW_STR_LAST(hdr);
483-
484-
T_DBG3("%s: add to hdr, hdr->len=%lu, last->len=%lu,"
485-
" last->data=%.*s\n", __func__, hdr->len, last->len,
486-
(int)last->len, last->data);
487-
488-
++hdr->len;
489-
if (!TFW_STR_PLAIN(hdr))
490-
++last->len;
547+
/*
548+
* If the new page was allocated or it->pos points to
549+
* the buffer, which was allocated to store previously
550+
* decoded header (it->to_alloc != 0) we should expand
551+
* header.
552+
*/
553+
if (!np && !it->to_alloc) {
554+
*it->pos++ = sym;
555+
ADJUST_HDR_LEN(it);
491556
return 0;
492557
}
493558

494559
r = tfw_hpack_exp_hdr(req->pool, 1, it);
495560
if (unlikely(r))
496561
return r;
497562

563+
*it->pos++ = sym;
564+
it->to_alloc = 0;
565+
498566
return 0;
499567
}
500568

@@ -506,28 +574,14 @@ huffman_decode_tail(TfwHPack *__restrict hp, TfwHttpReq *__restrict req,
506574
unsigned int i;
507575
int r;
508576

509-
#define ADJUST_EXTRA_RSPACE(req) \
510-
do { \
511-
TfwMsgParseIter *it = &req->pit; \
512-
TfwStr *hdr = &it->hdr; \
513-
TfwStr *last = TFW_STR_LAST(hdr); \
514-
\
515-
hdr->len -= it->rspace; \
516-
if (!TFW_STR_PLAIN(hdr)) \
517-
last->len -= it->rspace; \
518-
it->rspace = 0; \
519-
} while (0)
520-
521577
for (;;) {
522578
int shift;
523579

524580
if (hp->curr == -HT_NBITS) {
525-
if (likely(offset == 0)) {
526-
ADJUST_EXTRA_RSPACE(req);
581+
if (likely(offset == 0))
527582
return T_OK;
528-
} else {
583+
else
529584
return T_COMPRESSION;
530-
}
531585
}
532586

533587
i = (hp->hctx << -hp->curr) & HT_NMASK;
@@ -554,10 +608,8 @@ do { \
554608
*/
555609
if (likely(offset == 0)) {
556610
if ((i ^ (HT_EOS_HIGH >> 1)) <
557-
(1U << -hp->curr)) {
558-
ADJUST_EXTRA_RSPACE(req);
611+
(1U << -hp->curr))
559612
return T_OK;
560-
}
561613
}
562614
/*
563615
* The first condition here equivalent to the
@@ -581,10 +633,8 @@ do { \
581633
}
582634
}
583635
if (likely(offset == 0)) {
584-
if ((i ^ (HT_EOS_HIGH >> 1)) < (1U << -hp->curr)) {
585-
ADJUST_EXTRA_RSPACE(req);
636+
if ((i ^ (HT_EOS_HIGH >> 1)) < (1U << -hp->curr))
586637
return T_OK;
587-
}
588638
}
589639
return T_COMPRESSION;
590640

fw/hpack.h

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -296,17 +296,6 @@ typedef struct {
296296
TfwStr hdr_data;
297297
} TfwDecodeCacheIter;
298298

299-
#define BUFFER_GET(len, it) \
300-
do { \
301-
BUG_ON(!(len)); \
302-
WARN_ON_ONCE((it)->rspace); \
303-
(it)->rspace = len; \
304-
(it)->pos = tfw_pool_alloc_not_align((it)->pool, len); \
305-
T_DBG3("%s: get buffer, len=%lu, it->pos=[%p]," \
306-
" it->pos=%lu\n", __func__, (unsigned long)len, \
307-
(it)->pos, (unsigned long)(it)->pos); \
308-
} while (0)
309-
310299
void write_int(unsigned long index, unsigned short max, unsigned short mask,
311300
TfwHPackInt *__restrict res_idx);
312301
int tfw_hpack_init(TfwHPack *__restrict hp, unsigned int htbl_sz);

fw/msg.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,16 @@ typedef struct {
6767
* @pool - allocation pool for target buffer of decoded headers;
6868
* @parsed_hdr - pointer to the message header which is currently processed;
6969
* @hdrs_len - accumulated length of message's decoded and parsed headers;
70+
* @rspace - space remained in the allocated chunk;
71+
* @pos - pointer to the currently allocated chunk of decoded headers'
72+
* buffer;
7073
* @hdrs_cnt - count of all headers from message headers block;
7174
* @__off - offset for iterator reinitializing before next processing
7275
* stage;
7376
* @hdr - descriptor of currently decoded header in target buffer;
74-
* @pos - pointer to the currently allocated chunk of decoded headers'
75-
* buffer;
76-
* @rspace - space remained in the allocated chunk;
7777
* @nm_len - length of the decoded header's name;
78+
* @to_alloc - count of bytes which, should be allocated, after rspace
79+
* will be exceeded;
7880
* @nm_num - chunks number of the decoded header's name;
7981
* @tag - tag of currently processed decoded header.
8082
* @next - number of chunk which points to the decoded header part
@@ -84,12 +86,13 @@ typedef struct {
8486
TfwPool *pool;
8587
TfwStr *parsed_hdr;
8688
unsigned long hdrs_len;
89+
unsigned long rspace;
90+
char *pos;
8791
unsigned int hdrs_cnt;
8892
char __off[0];
8993
TfwStr hdr;
90-
char *pos;
91-
unsigned long rspace;
9294
unsigned long nm_len;
95+
unsigned long to_alloc;
9396
unsigned int nm_num;
9497
unsigned int tag;
9598
unsigned int next;

0 commit comments

Comments
 (0)