degesch: optimize buffer memory usage

We have approximately 5 formatter_items per buffer_line.  Let's assume
we're on a 64-bit machine.  Then there were (5 * 2) + 3 useless pointers
(104 bytes) as well as 5 * (4 + 4) = 40 bytes of wasted space because
of needless padding.  That's 144 bytes already.  Compared to that, this
change adds 16 bytes of overhead for an array sentinel, i.e. 128B less.

With a limit of 1000 lines per buffer, we've saved ~128kB per buffer
on completely useless data, and code complexity stays roughly the same.

All in all, memory usage for buffers should be about 50% lower.
This commit is contained in:
Přemysl Eric Janouch 2016-01-31 20:07:20 +01:00
parent 10a264ec3d
commit a850ee45f1
2 changed files with 41 additions and 35 deletions

2
NEWS
View File

@ -12,6 +12,8 @@
* degesch: backlog limit was made configurable
* degesch: optimize buffer memory usage
* kike: add support for IRCv3.2 server-time
* Remote addresses are now resolved asynchronously

View File

@ -968,6 +968,7 @@ REF_COUNTABLE_METHODS (channel)
enum formatter_item_type
{
FORMATTER_ITEM_END, ///< Sentinel value for arrays
FORMATTER_ITEM_TEXT, ///< Text
FORMATTER_ITEM_ATTR, ///< Formatting attributes
FORMATTER_ITEM_FG_COLOR, ///< Foreground color
@ -978,26 +979,16 @@ enum formatter_item_type
struct formatter_item
{
LIST_HEADER (struct formatter_item)
enum formatter_item_type type; ///< Type of this item
enum formatter_item_type type : 16; ///< Type of this item
int attribute : 16; ///< Attribute ID
int color; ///< Color
int attribute; ///< Attribute ID
char *text; ///< Either text or an attribute string
char *text; ///< String
};
static struct formatter_item *
formatter_item_new (void)
{
struct formatter_item *self = xcalloc (1, sizeof *self);
return self;
}
static void
formatter_item_destroy (struct formatter_item *self)
formatter_item_free (struct formatter_item *self)
{
free (self->text);
free (self);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1008,7 +999,8 @@ struct formatter
struct server *s; ///< Server
struct formatter_item *items; ///< Items
struct formatter_item *items_tail; ///< Tail of items
size_t items_len; ///< Items used
size_t items_alloc; ///< Items allocated
};
static void
@ -1018,13 +1010,16 @@ formatter_init (struct formatter *self,
memset (self, 0, sizeof *self);
self->ctx = ctx;
self->s = s;
self->items = xcalloc (sizeof *self->items, (self->items_alloc = 16));
self->items_len = 0;
}
static void
formatter_free (struct formatter *self)
{
LIST_FOR_EACH (struct formatter_item, iter, self->items)
formatter_item_destroy (iter);
for (size_t i = 0; i < self->items_len; i++)
formatter_item_free (&self->items[i]);
free (self->items);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1045,21 +1040,31 @@ struct buffer_line
int flags; ///< Flags
time_t when; ///< Time of the event
struct formatter formatter; ///< Line data
struct formatter_item items[]; ///< Line data
};
/// Create a new buffer line stealing all data from the provided formatter
struct buffer_line *
buffer_line_new (struct formatter *f)
{
struct buffer_line *self = xcalloc (1, sizeof *self);
self->formatter = *f;
// We make space for one more item that gets initialized to all zeros,
// meaning FORMATTER_ITEM_END (because it's the first value in the enum)
size_t items_size = f->items_len * sizeof *f->items;
struct buffer_line *self =
xcalloc (1, sizeof *self + items_size + sizeof *self->items);
memcpy (self->items, f->items, items_size);
// We've stolen pointers from the formatter, let's destroy it altogether
free (f->items);
memset (f, 0, sizeof *f);
return self;
}
static void
buffer_line_destroy (struct buffer_line *self)
{
formatter_free (&self->formatter);
for (struct formatter_item *iter = self->items; iter->type; iter++)
formatter_item_free (iter);
free (self);
}
@ -2619,9 +2624,10 @@ formatter_add_item (struct formatter *self, struct formatter_item template_)
if (template_.text)
template_.text = xstrdup (template_.text);
struct formatter_item *item = formatter_item_new ();
*item = template_;
LIST_APPEND_WITH_TAIL (self->items, self->items_tail, item);
if (self->items_len == self->items_alloc)
self->items = xreallocarray
(self->items, sizeof *self->items, (self->items_alloc <<= 1));
self->items[self->items_len++] = template_;
}
#define FORMATTER_ADD_ITEM(self, type_, ...) formatter_add_item ((self), \
@ -2911,13 +2917,6 @@ formatter_add (struct formatter *self, const char *format, ...)
va_end (ap);
}
static void
formatter_add_from (struct formatter *self, struct formatter *other)
{
for (struct formatter_item *iter = other->items; iter; iter = iter->next)
formatter_add_item (self, *iter);
}
static bool
formatter_flush_attr
(struct attribute_printer *state, struct formatter_item *item)
@ -2969,9 +2968,12 @@ formatter_flush (struct formatter *self, FILE *stream, bool raw_attributes)
{
if (!raw_attributes && !get_attribute_printer (stream))
{
LIST_FOR_EACH (struct formatter_item, iter, self->items)
for (size_t i = 0; i < self->items_len; i++)
{
struct formatter_item *iter = &self->items[i];
if (iter->type == FORMATTER_ITEM_TEXT)
fputs (iter->text, stream);
}
return;
}
@ -2980,8 +2982,9 @@ formatter_flush (struct formatter *self, FILE *stream, bool raw_attributes)
attribute_printer_reset (&state);
int attribute_ignore = 0;
LIST_FOR_EACH (struct formatter_item, iter, self->items)
for (size_t i = 0; i < self->items_len; i++)
{
struct formatter_item *iter = &self->items[i];
if (iter->type == FORMATTER_ITEM_TEXT)
formatter_flush_text (self->ctx, iter->text, stream);
else if (iter->type == FORMATTER_ITEM_IGNORE_ATTR)
@ -3051,7 +3054,9 @@ buffer_line_flush (struct buffer_line *line, struct formatter *f, FILE *output,
if (flags & BUFFER_LINE_STATUS) formatter_add (f, " - ");
if (flags & BUFFER_LINE_ERROR) formatter_add (f, "#a=!=#r ", ATTR_ERROR);
formatter_add_from (f, &line->formatter);
for (struct formatter_item *iter = line->items; iter->type; iter++)
formatter_add_item (f, *iter);
formatter_add (f, "\n");
formatter_flush (f, output, raw_attributes);
formatter_free (f);
@ -3141,7 +3146,6 @@ log_formatter (struct app_context *ctx,
if (!buffer)
buffer = ctx->global_buffer;
// Move the formatter inside
struct buffer_line *line = buffer_line_new (f);
line->flags = flags;
// TODO: allow providing custom time (IRCv3.2 server-time)