On Wed, 16 Jul 2014, David Meyer wrote:

I've found that curl_multi slows down quadratically with the number of running requests.

Hey again,

I suggest a patch like the attached one. It'd be great if you could try this on your use case and see how it behaves!

--

 / daniel.haxx.se
From 3c8c873252fa486d1e57fa28b8b455bfdb487f77 Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <dan...@haxx.se>
Date: Tue, 2 Sep 2014 09:29:50 +0200
Subject: [PATCH] multi: convert CURLM_STATE_CONNECT_PEND handling to a list

... instead of scanning through all handles, stash only the actual
handles that are in that state in the new ->pending list and scan that
list only. It should be mostly empty or very short. And only used for
pipelining.

This avoids a rather hefty slow-down especially notable if you add many
handles to the same multi handle. Regression introduced in commit
0f147887 (version 7.30.0).

Bug: http://curl.haxx.se/mail/lib-2014-07/0206.html
Reported-by: David Meyer
---
 lib/multi.c       | 27 +++++++++++++++++++++------
 lib/multihandle.h |  5 ++++-
 2 files changed, 25 insertions(+), 7 deletions(-)

diff --git a/lib/multi.c b/lib/multi.c
index 557be06..7947436 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -307,10 +307,14 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
 
   multi->msglist = Curl_llist_alloc(multi_freeamsg);
   if(!multi->msglist)
     goto error;
 
+  multi->pending = Curl_llist_alloc(multi_freeamsg);
+  if(!multi->pending)
+    goto error;
+
   /* allocate a new easy handle to use when closing cached connections */
   multi->closure_handle = curl_easy_init();
   if(!multi->closure_handle)
     goto error;
 
@@ -332,10 +336,11 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
   Curl_conncache_destroy(multi->conn_cache);
   multi->conn_cache = NULL;
   Curl_close(multi->closure_handle);
   multi->closure_handle = NULL;
   Curl_llist_destroy(multi->msglist, NULL);
+  Curl_llist_destroy(multi->pending, NULL);
 
   free(multi);
   return NULL;
 }
 
@@ -1044,11 +1049,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
                                   &async, &protocol_connect);
       if(CURLE_NO_CONNECTION_AVAILABLE == data->result) {
         /* There was no connection available. We will go to the pending
            state and wait for an available connection. */
         multistate(data, CURLM_STATE_CONNECT_PEND);
-        data->result = CURLE_OK;
+
+        /* add this handle to the list of connect-pending handles */
+        if(!Curl_llist_insert_next(multi->pending, multi->pending->tail, data))
+          data->result = CURLM_OUT_OF_MEMORY;
+        else
+          data->result = CURLE_OK;
         break;
       }
 
       if(CURLE_OK == data->result) {
         /* Add this handle to the send or pend pipeline */
@@ -1882,10 +1892,14 @@ CURLMcode curl_multi_cleanup(CURLM *multi_handle)
 
     /* remove the pending list of messages */
     Curl_llist_destroy(multi->msglist, NULL);
     multi->msglist = NULL;
 
+    /* remove the pending handles queue */
+    Curl_llist_destroy(multi->pending, NULL);
+    multi->msglist = NULL;
+
     /* remove all easy handles */
     data = multi->easyp;
     while(data) {
       nextdata=data->next;
       if(data->dns.hostcachetype == HCACHE_MULTI) {
@@ -2774,20 +2788,21 @@ struct curl_llist *Curl_multi_pipelining_server_bl(struct Curl_multi *multi)
   return multi->pipelining_server_bl;
 }
 
 void Curl_multi_process_pending_handles(struct Curl_multi *multi)
 {
-  struct SessionHandle *data;
+  struct curl_llist_element *e;
 
-  data=multi->easyp;
-  while(data) {
+  for(e = multi->pending->head; e; e = e->next) {
+    struct SessionHandle *data = e->ptr;
     if(data->mstate == CURLM_STATE_CONNECT_PEND) {
       multistate(data, CURLM_STATE_CONNECT);
+      /* Remove this node from the list */
+      Curl_llist_remove(multi->pending, e, NULL);
       /* Make sure that the handle will be processed soonish. */
-      Curl_expire(data, 1);
+      Curl_expire_latest(data, 1);
     }
-    data = data->next; /* operate on next handle */
   }
 }
 
 #ifdef DEBUGBUILD
 void Curl_multi_dump(const struct Curl_multi *multi_handle)
diff --git a/lib/multihandle.h b/lib/multihandle.h
index 552aa93..1a4b1d9 100644
--- a/lib/multihandle.h
+++ b/lib/multihandle.h
@@ -5,11 +5,11 @@
  *  Project                     ___| | | |  _ \| |
  *                             / __| | | | |_) | |
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <dan...@haxx.se>, et al.
+ * Copyright (C) 1998 - 2014, Daniel Stenberg, <dan...@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
  * are also available at http://curl.haxx.se/docs/copyright.html.
  *
@@ -73,10 +73,13 @@ struct Curl_multi {
   int num_alive; /* amount of easy handles that are added but have not yet
                     reached COMPLETE state */
 
   struct curl_llist *msglist; /* a list of messages from completed transfers */
 
+  struct curl_llist *pending; /* SessionHandles that are in the
+                                 CURLM_STATE_CONNECT_PEND state */
+
   /* callback function and user data pointer for the *socket() API */
   curl_socket_callback socket_cb;
   void *socket_userp;
 
   /* Hostname cache */
-- 
2.1.0

-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette:  http://curl.haxx.se/mail/etiquette.html

Reply via email to