After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 790275 - avoid temporary string allocations in g_resources_enumerate_children
avoid temporary string allocations in g_resources_enumerate_children
Status: RESOLVED FIXED
Product: glib
Classification: Platform
Component: gio
unspecified
Other Linux
: Normal normal
: ---
Assigned To: gtkdev
gtkdev
Depends on:
Blocks:
 
 
Reported: 2017-11-13 07:09 UTC by Christian Hergert
Modified: 2017-11-15 11:25 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
avoid string allocations in g_resources_enumerate_children (2.15 KB, patch)
2017-11-13 07:10 UTC, Christian Hergert
needs-work Details | Review
another approach (4.99 KB, patch)
2017-11-13 14:39 UTC, Matthias Clasen
none Details | Review
another approach, 2 (1.27 KB, patch)
2017-11-13 14:40 UTC, Matthias Clasen
none Details | Review
gresource: avoid allocations in enumerate_children() (2.82 KB, patch)
2017-11-14 11:16 UTC, Christian Hergert
none Details | Review
gresource: avoid allocations in enumerate_children() (4.34 KB, patch)
2017-11-14 23:30 UTC, Christian Hergert
committed Details | Review

Description Christian Hergert 2017-11-13 07:09:47 UTC
The g_resources_enumerate_children() function can get called a great deal. In the startup of Builder, the g_strconcat() and g_strdup() were responsible for >6mb of allocations.

Patch forthcoming.
Comment 1 Christian Hergert 2017-11-13 07:10:17 UTC
Created attachment 363471 [details] [review]
avoid string allocations in g_resources_enumerate_children
Comment 2 Philip Withnall 2017-11-13 10:22:53 UTC
Review of attachment 363471 [details] [review]:

::: gio/gresource.c
@@ +848,3 @@
                                GError               **error)
 {
+  gchar local_str[256];

Is it worth using PATH_MAX for this if defined?

@@ +866,3 @@
   if (path[path_len-1] != '/')
+    {
+      if (path_len < (sizeof local_str - 2))

Brackets for sizeof don’t match the coding style:

if (path_len < sizeof (local_str) - 2)

@@ +871,3 @@
+          local_str[path_len] = '/';
+          local_str[path_len+1] = 0;
+          path_with_slash = local_str;

I’m not so happy about this code path; it seems to be too much of a special case. If it’s going in, it needs a comment explaining why it’s a necessary fast path.

Could you change Builder to ensure that all the paths it passes in have a trailing slash? That would avoid the memcpy() as well.
Comment 3 Philip Withnall 2017-11-13 10:31:40 UTC
Review of attachment 363471 [details] [review]:

Please also ensure that the unit tests for g_resource_enumerate_children() cover calls with and without a trailing slash, and with path lengths greater than the size of the buffer on the stack.
Comment 4 Christian Hergert 2017-11-13 12:44:32 UTC
Review of attachment 363471 [details] [review]:

::: gio/gresource.c
@@ +848,3 @@
                                GError               **error)
 {
+  gchar local_str[256];

PATH_MAX can't be trusted and can even be max uint (not sure if we support anywhere with that). 256 was arbitrarily chosen as the size of a single cacheline on modern intel which would be "sufficient large for all paths ive used".

@@ +871,3 @@
+          local_str[path_len] = '/';
+          local_str[path_len+1] = 0;
+          path_with_slash = local_str;

Well the point of it was that g_strconcat() was showing up really high on my memory profiles as an offender of transient allocations. I would have used g_snprintf(), but what's the point in parsing a string to do a copy set two bytes.

We should still change things in our various projects to avoid it though.
Comment 5 Philip Withnall 2017-11-13 12:58:31 UTC
Review of attachment 363471 [details] [review]:

::: gio/gresource.c
@@ +848,3 @@
                                GError               **error)
 {
+  gchar local_str[256];

OK, but please stick in a brief comment justifying why 256 was chosen (i.e. basically what you said here).

@@ +871,3 @@
+          local_str[path_len] = '/';
+          local_str[path_len+1] = 0;
+          path_with_slash = local_str;

I know what the point of it is; other people reading the code later might not. The comment need not be verbose, but should be present.
Comment 6 Matthias Clasen 2017-11-13 14:39:08 UTC
Created attachment 363512 [details] [review]
another approach
Comment 7 Matthias Clasen 2017-11-13 14:40:04 UTC
Created attachment 363513 [details] [review]
another approach, 2
Comment 8 Matthias Clasen 2017-11-13 14:40:43 UTC
maybe even more hacky, but it entirely avoids copying the string, in all cases
Comment 9 Philip Withnall 2017-11-13 15:04:11 UTC
(In reply to Matthias Clasen from comment #8)
> maybe even more hacky, but it entirely avoids copying the string, in all
> cases

I far prefer Christian’s approach. Changing gvdb just for GResource is way too invasive. It doesn’t look like this last_byte argument could usefully be used by any other code which calls gvdb (ie. GSettings).

I’m more interested to know how feasible it is to change Builder to always include the trailing slash in its calls, and circumvent the need for all of this. (To be clear, I still think it makes sense to go with Christian’s patch, even if we do fix Builder, since this is likely a hot path for any other large GTK+ app.)
Comment 10 Matthias Clasen 2017-11-13 16:21:43 UTC
most of these calls come out of GtkIconTheme, I assume, where we share the same path for icon cache lookups and resource lookups. It should in theory be possible to make all the paths have a trailing /, but I'm quite worried that there will be fallout. GtkIconTheme is quite messy
Comment 11 Matthias Clasen 2017-11-13 17:15:22 UTC
I've taken care of all the places I could find in gtk now. Maybe that is good enough. Christian ?
Comment 12 Christian Hergert 2017-11-14 11:16:50 UTC
Created attachment 363573 [details] [review]
gresource: avoid allocations in enumerate_children()

In the vast majority of cases, we can avoid temporary
allocations for paths in g_resources_enumerate_children().

In the case we need to add a suffix "/", we can usually just
build the path on the stack. In other cases, we can completely
avoid the strdup, which appears to only have been added for
readability. If the path is really long, we fallback to doing
what we did before, and use g_strconcat().

In the case of Builder, this saved 5.3mb of temporary
allocations in the process of showing the first application
window.
Comment 13 Christian Hergert 2017-11-14 11:21:46 UTC
(In reply to Matthias Clasen from comment #11)
> I've taken care of all the places I could find in gtk now. Maybe that is
> good enough. Christian ?

It's probably fine. Builder and libdazzle where doing some pretty bad things because I had no idea of the need to have a trailing slash. Libpeas, which also scans resources aggressively only joins the results of this function, so it should get the trailing slash automatically.

I don't care which approach we take since I don't have to maintain it, but I've uploaded another patch with the nits addressed.
Comment 14 Philip Withnall 2017-11-14 14:05:50 UTC
Review of attachment 363573 [details] [review]:

(In reply to Philip Withnall from comment #3)
> Review of attachment 363471 [details] [review] [review]:
> 
> Please also ensure that the unit tests for g_resource_enumerate_children()
> cover calls with and without a trailing slash, and with path lengths greater
> than the size of the buffer on the stack.

Still need some tests, please. (Sorry, my original comment was not very obvious.)

The rest looks good to me.
Comment 15 Christian Hergert 2017-11-14 23:30:17 UTC
Created attachment 363645 [details] [review]
gresource: avoid allocations in enumerate_children()

In the vast majority of cases, we can avoid temporary
allocations for paths in g_resources_enumerate_children().

In the case we need to add a suffix "/", we can usually just
build the path on the stack. In other cases, we can completely
avoid the strdup, which appears to only have been added for
readability. If the path is really long, we fallback to doing
what we did before, and use g_strconcat().

In the case of Builder, this saved 5.3mb of temporary
allocations in the process of showing the first application
window.
Comment 16 Philip Withnall 2017-11-15 08:26:15 UTC
Review of attachment 363645 [details] [review]:

Great, thanks.
Comment 17 Christian Hergert 2017-11-15 11:24:54 UTC
Attachment 363645 [details] pushed as 5464461 - gresource: avoid allocations in enumerate_children()