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 668842 - [GSocket] Add caching for the sender address in g_socket_receive_from()
[GSocket] Add caching for the sender address in g_socket_receive_from()
Status: RESOLVED FIXED
Product: glib
Classification: Platform
Component: network
2.31.x
Other Linux
: Normal major
: ---
Assigned To: gtkdev
gtkdev
Depends on:
Blocks:
 
 
Reported: 2012-01-27 14:19 UTC by Sebastian Dröge (slomo)
Modified: 2012-11-11 16:33 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Add caching for the receiver addresses for g_socket_receive_from() (4.62 KB, patch)
2012-11-09 15:14 UTC, Sebastian Dröge (slomo)
reviewed Details | Review
Add caching for the receiver addresses for g_socket_receive_from() (4.34 KB, patch)
2012-11-09 16:46 UTC, Sebastian Dröge (slomo)
committed Details | Review

Description Sebastian Dröge (slomo) 2012-01-27 14:19:12 UTC
Currently a new GSocketAddress is instantiated for every single packet that is ever received on the socket. This is a serious performance problem if you receive at a very high packet rate.

Usually you don't receive from a new sender every packet so a possible solution would be to add caching for the GSocketAddresses in GSocket, best with a configurable cache size. When using good, old recvfrom() there's no performance problem because the address will usually be allocated on the stack and you don't need to go through the GType system, etc.
Comment 1 André Klapper 2012-01-27 16:09:13 UTC
(Hmm, did you set the ancient 2.25 version on purpose for this report?)
Comment 2 Sebastian Dröge (slomo) 2012-01-27 16:12:48 UTC
(In reply to comment #1)
> (Hmm, did you set the ancient 2.25 version on purpose for this report?)

No, that's what was chosen by bugzilla or my browser or whatever. I'm using latest GIT master
Comment 3 Dan Winship 2012-01-30 13:49:37 UTC
(In reply to comment #0)
> Usually you don't receive from a new sender every packet so a possible solution
> would be to add caching for the GSocketAddresses in GSocket

Or in g_socket_address_new_from_native()
Comment 4 Dan Winship 2012-01-30 13:58:57 UTC
Sebastian, please file networking-related bugs against the "network" component, not "gio"
Comment 5 Olivier Crête 2012-05-29 01:38:45 UTC
I'm actually hitting this in libnice, the object creation was actually using 30% of my CPU (while sending 25MB/s in ~1500 bytes packets). So this is important enough that we're considering fishing out the fd and using recvfrom() directly instead.
Comment 6 Tim-Philipp Müller 2012-07-24 18:43:49 UTC
<tpm> (...) https://bugzilla.gnome.org/show_bug.cgi?id=668842 (...) is a huge problem for us right now
<danw> tpm: ah, yeah, i started poking at that once, and then realized i needed to make it thread-safe, and then went off and did something else
<danw> i'm happy to take a patch for it
<tpm> ah, cool
<tpm> do you have a preference how/where to start poking?
<danw> tpm: well, like i said, i think there's an argument for putting it in g_socket_address_new_from_native(), but otoh, if you put it into GSocket instead then you don't need to worry about threads. And no one has complained about this problem in connection with any APIs other than g_socket_receive_from()...
<tpm> e.g. just make it internal to the socket, or expose it or make it global or ..
<danw> make it internal to the socket
<tpm> alright, thanks
<danw> and per-socket
<danw> if per-socket doesn't work, then i'd say put it in g_socket_address_new_from_native() using a single global mutex-protected cache
<tpm> great (..)
Comment 7 Olivier Crête 2012-08-13 23:44:23 UTC
For APIs that use GSocket internally but that don't use GSocketAddress in their API, we hit the same problem on the send side (in libnice, we have our own cache because of that).
Comment 8 Sebastian Dröge (slomo) 2012-11-09 15:14:45 UTC
Created attachment 228578 [details] [review]
Add caching for the receiver addresses for g_socket_receive_from()
Comment 9 Sebastian Dröge (slomo) 2012-11-09 15:18:26 UTC
In my testcase this is improving the performance by 50%:

gst-launch-1.0 fakesrc sizetype=fixed sizemax=188 ! udpsink port=5123
time gst-launch-1.0 udpsrc uri=udp://127.0.0.1:5123 ! fakesink num-buffers=500000

Goes from
real	0m12.198s
user	0m10.233s
sys	0m1.868s
to
real	0m5.710s
user	0m3.024s
sys	0m2.192s


The explicit storage of the native socket address inside the cache is done because calling g_socket_address_to_native() shows up with ~23% of instructions inside g_socket_address_receive_from() in callgrind.
Comment 10 Tim-Philipp Müller 2012-11-09 15:26:51 UTC
Out of curiosity, what numbers do you get with the native API (i.e. 0.10) ?
Comment 11 Sebastian Dröge (slomo) 2012-11-09 15:31:43 UTC
real	0m4.856s
user	0m3.988s
sys	0m0.544s
Comment 12 Dan Winship 2012-11-09 16:14:53 UTC
Comment on attachment 228578 [details] [review]
Add caching for the receiver addresses for g_socket_receive_from()

>+/* Size of the receiver cache for g_socket_receive_from() */
>+#define RECV_ADDR_CACHE_SIZE 8

Is that based on anything in particular? Did you test other sizes?

>+    gpointer naddr;
>+    gint naddrlen;

I'd prefer

  struct sockaddr *native;
  gsize native_len;

(That applies pretty much everywhere you currently have "naddr".)

>+static GSocketAddress *
>+cache_recv_address (GSocket * socket, gpointer addr, int addrlen)

"GSocket *socket" (no space after "*"). (and again struct sockaddr * and gsize).

(I don't love the function name, but I don't have any especially better suggestions.)

>+  if (addrlen > 0)

Just do

    if (addrlen <= 0)
      return NULL;

and then you don't need to indent the rest of the function an extra level

>+          if (memcmp (tmp_naddr, addr, addrlen) == 0)
>+            {
>+              saddr = g_object_ref (tmp);
>+              socket->priv->recv_addr_cache[i].last_used = g_get_monotonic_time ();
>+              break;
>+            }

likewise, you can just "return saddr" instead of breaking here, and then you don't need the "if (!saddr)" below

>+          for (i = 0; i < RECV_ADDR_CACHE_SIZE; i++)
>+            {
>+              if (socket->priv->recv_addr_cache[i].last_used < oldest_time)

you could merge this into the previous loop so you're only iterating the cache once.
Comment 13 Sebastian Dröge (slomo) 2012-11-09 16:25:07 UTC
(In reply to comment #12)
> (From update of attachment 228578 [details] [review])
> >+/* Size of the receiver cache for g_socket_receive_from() */
> >+#define RECV_ADDR_CACHE_SIZE 8
> 
> Is that based on anything in particular? Did you test other sizes?

I didn't test any other sizes and had no scientific reason for chosing 8, just a feeling ;)

> >+    gpointer naddr;
> >+    gint naddrlen;
> 
> I'd prefer
> 
>   struct sockaddr *native;
>   gsize native_len;
> 
> (That applies pretty much everywhere you currently have "naddr".)

Alright, will change that later/tomorrow... and also all the other comments. This was just a proof of concept first so I didn't spend that much time in cleaning up the code before getting more comments about it going in the right direction.
Comment 14 Sebastian Dröge (slomo) 2012-11-09 16:46:24 UTC
Created attachment 228594 [details] [review]
Add caching for the receiver addresses for g_socket_receive_from()
Comment 15 Dan Winship 2012-11-11 16:18:13 UTC
Comment on attachment 228594 [details] [review]
Add caching for the receiver addresses for g_socket_receive_from()

just one tiny nit:

>+cache_recv_address (GSocket *socket, struct sockaddr * native, int native_len)

no space after the second "*"

otherwise ok to commit
Comment 16 Sebastian Dröge (slomo) 2012-11-11 16:33:55 UTC
Thanks for the fast reviews :) Is there a indent script or something for the GLib coding style?


commit 2bba1da30674686960571603961e8daed973e5d0
Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
Date:   Fri Nov 9 15:28:36 2012 +0100

    Add caching for the receiver addresses for g_socket_receive_from()
    
    https://bugzilla.gnome.org/show_bug.cgi?id=668842