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:
parent
10a264ec3d
commit
a850ee45f1
2
NEWS
2
NEWS
|
@ -12,6 +12,8 @@
|
||||||
|
|
||||||
* degesch: backlog limit was made configurable
|
* degesch: backlog limit was made configurable
|
||||||
|
|
||||||
|
* degesch: optimize buffer memory usage
|
||||||
|
|
||||||
* kike: add support for IRCv3.2 server-time
|
* kike: add support for IRCv3.2 server-time
|
||||||
|
|
||||||
* Remote addresses are now resolved asynchronously
|
* Remote addresses are now resolved asynchronously
|
||||||
|
|
74
degesch.c
74
degesch.c
|
@ -968,6 +968,7 @@ REF_COUNTABLE_METHODS (channel)
|
||||||
|
|
||||||
enum formatter_item_type
|
enum formatter_item_type
|
||||||
{
|
{
|
||||||
|
FORMATTER_ITEM_END, ///< Sentinel value for arrays
|
||||||
FORMATTER_ITEM_TEXT, ///< Text
|
FORMATTER_ITEM_TEXT, ///< Text
|
||||||
FORMATTER_ITEM_ATTR, ///< Formatting attributes
|
FORMATTER_ITEM_ATTR, ///< Formatting attributes
|
||||||
FORMATTER_ITEM_FG_COLOR, ///< Foreground color
|
FORMATTER_ITEM_FG_COLOR, ///< Foreground color
|
||||||
|
@ -978,26 +979,16 @@ enum formatter_item_type
|
||||||
|
|
||||||
struct formatter_item
|
struct formatter_item
|
||||||
{
|
{
|
||||||
LIST_HEADER (struct formatter_item)
|
enum formatter_item_type type : 16; ///< Type of this item
|
||||||
|
int attribute : 16; ///< Attribute ID
|
||||||
enum formatter_item_type type; ///< Type of this item
|
|
||||||
int color; ///< Color
|
int color; ///< Color
|
||||||
int attribute; ///< Attribute ID
|
char *text; ///< String
|
||||||
char *text; ///< Either text or an attribute string
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct formatter_item *
|
|
||||||
formatter_item_new (void)
|
|
||||||
{
|
|
||||||
struct formatter_item *self = xcalloc (1, sizeof *self);
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
formatter_item_destroy (struct formatter_item *self)
|
formatter_item_free (struct formatter_item *self)
|
||||||
{
|
{
|
||||||
free (self->text);
|
free (self->text);
|
||||||
free (self);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -1008,7 +999,8 @@ struct formatter
|
||||||
struct server *s; ///< Server
|
struct server *s; ///< Server
|
||||||
|
|
||||||
struct formatter_item *items; ///< Items
|
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
|
static void
|
||||||
|
@ -1018,13 +1010,16 @@ formatter_init (struct formatter *self,
|
||||||
memset (self, 0, sizeof *self);
|
memset (self, 0, sizeof *self);
|
||||||
self->ctx = ctx;
|
self->ctx = ctx;
|
||||||
self->s = s;
|
self->s = s;
|
||||||
|
self->items = xcalloc (sizeof *self->items, (self->items_alloc = 16));
|
||||||
|
self->items_len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
formatter_free (struct formatter *self)
|
formatter_free (struct formatter *self)
|
||||||
{
|
{
|
||||||
LIST_FOR_EACH (struct formatter_item, iter, self->items)
|
for (size_t i = 0; i < self->items_len; i++)
|
||||||
formatter_item_destroy (iter);
|
formatter_item_free (&self->items[i]);
|
||||||
|
free (self->items);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -1045,21 +1040,31 @@ struct buffer_line
|
||||||
|
|
||||||
int flags; ///< Flags
|
int flags; ///< Flags
|
||||||
time_t when; ///< Time of the event
|
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 *
|
struct buffer_line *
|
||||||
buffer_line_new (struct formatter *f)
|
buffer_line_new (struct formatter *f)
|
||||||
{
|
{
|
||||||
struct buffer_line *self = xcalloc (1, sizeof *self);
|
// We make space for one more item that gets initialized to all zeros,
|
||||||
self->formatter = *f;
|
// 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;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
buffer_line_destroy (struct buffer_line *self)
|
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);
|
free (self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2619,9 +2624,10 @@ formatter_add_item (struct formatter *self, struct formatter_item template_)
|
||||||
if (template_.text)
|
if (template_.text)
|
||||||
template_.text = xstrdup (template_.text);
|
template_.text = xstrdup (template_.text);
|
||||||
|
|
||||||
struct formatter_item *item = formatter_item_new ();
|
if (self->items_len == self->items_alloc)
|
||||||
*item = template_;
|
self->items = xreallocarray
|
||||||
LIST_APPEND_WITH_TAIL (self->items, self->items_tail, item);
|
(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), \
|
#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);
|
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
|
static bool
|
||||||
formatter_flush_attr
|
formatter_flush_attr
|
||||||
(struct attribute_printer *state, struct formatter_item *item)
|
(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))
|
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)
|
if (iter->type == FORMATTER_ITEM_TEXT)
|
||||||
fputs (iter->text, stream);
|
fputs (iter->text, stream);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2980,8 +2982,9 @@ formatter_flush (struct formatter *self, FILE *stream, bool raw_attributes)
|
||||||
attribute_printer_reset (&state);
|
attribute_printer_reset (&state);
|
||||||
|
|
||||||
int attribute_ignore = 0;
|
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)
|
if (iter->type == FORMATTER_ITEM_TEXT)
|
||||||
formatter_flush_text (self->ctx, iter->text, stream);
|
formatter_flush_text (self->ctx, iter->text, stream);
|
||||||
else if (iter->type == FORMATTER_ITEM_IGNORE_ATTR)
|
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_STATUS) formatter_add (f, " - ");
|
||||||
if (flags & BUFFER_LINE_ERROR) formatter_add (f, "#a=!=#r ", ATTR_ERROR);
|
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_add (f, "\n");
|
||||||
formatter_flush (f, output, raw_attributes);
|
formatter_flush (f, output, raw_attributes);
|
||||||
formatter_free (f);
|
formatter_free (f);
|
||||||
|
@ -3141,7 +3146,6 @@ log_formatter (struct app_context *ctx,
|
||||||
if (!buffer)
|
if (!buffer)
|
||||||
buffer = ctx->global_buffer;
|
buffer = ctx->global_buffer;
|
||||||
|
|
||||||
// Move the formatter inside
|
|
||||||
struct buffer_line *line = buffer_line_new (f);
|
struct buffer_line *line = buffer_line_new (f);
|
||||||
line->flags = flags;
|
line->flags = flags;
|
||||||
// TODO: allow providing custom time (IRCv3.2 server-time)
|
// TODO: allow providing custom time (IRCv3.2 server-time)
|
||||||
|
|
Loading…
Reference in New Issue