The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/2062

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===
Here's some more work on #1890, namely, to use a larger buffer to cut down on the CPU work needed to do by LXD on a per-frame basis for websocket frames.

Note that I played with various websocket parameters here (in addition to and independent of) this one, see the top two commits here: https://github.com/tych0/lxd/commits/buffering-extras

But this is the one that had the biggest effect. Using bigger buffers for the websocket i/o itself actually slowed things down.

Also note that I chose a 4MB buffer here. Larger buffers (I tested up to 128MB) seem to speed things up, but only marginally, and 4MB is enough to capture most of the speedup (>50s down to about 25-28s).

Finally, there is one more thing I can think to try here, so I'm still not closing that issue, as we may be able to speed things up a bit more.
From a72bce375117aa73895b61b504600c4019fb5a82 Mon Sep 17 00:00:00 2001
From: Tycho Andersen <tycho.ander...@canonical.com>
Date: Mon, 23 May 2016 15:21:23 -0600
Subject: [PATCH 1/2] use some buffering for zfs/btrfs send

For now, let's use a 4MB buffer.

Signed-off-by: Tycho Andersen <tycho.ander...@canonical.com>
---
 client.go             |  4 ++--
 lxd/container_exec.go |  2 +-
 lxd/storage_btrfs.go  |  2 +-
 lxd/storage_zfs.go    |  2 +-
 shared/network.go     | 10 +++++++---
 shared/util.go        | 22 ++++++++++++++++------
 6 files changed, 28 insertions(+), 14 deletions(-)

diff --git a/client.go b/client.go
index d1e08cb..213b9cb 100644
--- a/client.go
+++ b/client.go
@@ -1450,7 +1450,7 @@ func (c *Client) Exec(name string, cmd []string, env 
map[string]string,
                        return -1, err
                }
 
-               shared.WebsocketSendStream(conn, stdin)
+               shared.WebsocketSendStream(conn, stdin, -1)
                <-shared.WebsocketRecvStream(stdout, conn)
                conn.Close()
 
@@ -1464,7 +1464,7 @@ func (c *Client) Exec(name string, cmd []string, env 
map[string]string,
                }
                defer conns[0].Close()
 
-               dones[0] = shared.WebsocketSendStream(conns[0], stdin)
+               dones[0] = shared.WebsocketSendStream(conns[0], stdin, -1)
 
                outputs := []io.WriteCloser{stdout, stderr}
                for i := 1; i < 3; i++ {
diff --git a/lxd/container_exec.go b/lxd/container_exec.go
index 2e70605..71aee79 100644
--- a/lxd/container_exec.go
+++ b/lxd/container_exec.go
@@ -204,7 +204,7 @@ func (s *execWs) Do(op *operation) error {
                                        <-shared.WebsocketRecvStream(ttys[i], 
s.conns[i])
                                        ttys[i].Close()
                                } else {
-                                       
<-shared.WebsocketSendStream(s.conns[i], ptys[i])
+                                       
<-shared.WebsocketSendStream(s.conns[i], ptys[i], -1)
                                        ptys[i].Close()
                                        wgEOF.Done()
                                }
diff --git a/lxd/storage_btrfs.go b/lxd/storage_btrfs.go
index e9903c0..298864d 100644
--- a/lxd/storage_btrfs.go
+++ b/lxd/storage_btrfs.go
@@ -850,7 +850,7 @@ func (s *btrfsMigrationSourceDriver) send(conn 
*websocket.Conn, btrfsPath string
                return err
        }
 
-       <-shared.WebsocketSendStream(conn, stdout)
+       <-shared.WebsocketSendStream(conn, stdout, 4 * 1024 * 1024)
 
        output, err := ioutil.ReadAll(stderr)
        if err != nil {
diff --git a/lxd/storage_zfs.go b/lxd/storage_zfs.go
index d138918..3604447 100644
--- a/lxd/storage_zfs.go
+++ b/lxd/storage_zfs.go
@@ -1227,7 +1227,7 @@ func (s *zfsMigrationSourceDriver) send(conn 
*websocket.Conn, zfsName string, zf
                return err
        }
 
-       <-shared.WebsocketSendStream(conn, stdout)
+       <-shared.WebsocketSendStream(conn, stdout, 4 * 1024 * 1024)
 
        output, err := ioutil.ReadAll(stderr)
        if err != nil {
diff --git a/shared/network.go b/shared/network.go
index f64e5fe..08e7019 100644
--- a/shared/network.go
+++ b/shared/network.go
@@ -115,7 +115,7 @@ func IsLoopback(iface *net.Interface) bool {
        return int(iface.Flags&net.FlagLoopback) > 0
 }
 
-func WebsocketSendStream(conn *websocket.Conn, r io.Reader) chan bool {
+func WebsocketSendStream(conn *websocket.Conn, r io.Reader, bufferSize int) 
chan bool {
        ch := make(chan bool)
 
        if r == nil {
@@ -124,7 +124,7 @@ func WebsocketSendStream(conn *websocket.Conn, r io.Reader) 
chan bool {
        }
 
        go func(conn *websocket.Conn, r io.Reader) {
-               in := ReaderToChannel(r)
+               in := ReaderToChannel(r, bufferSize)
                for {
                        buf, ok := <-in
                        if !ok {
@@ -244,7 +244,11 @@ func WebsocketMirror(conn *websocket.Conn, w 
io.WriteCloser, r io.ReadCloser) (c
        }(conn, w)
 
        go func(conn *websocket.Conn, r io.ReadCloser) {
-               in := ReaderToChannel(r)
+               /* For now, we don't need to adjust buffer sizes in
+                * WebsocketMirror, since it's used for interactive things like
+                * exec.
+                */
+               in := ReaderToChannel(r, -1)
                for {
                        buf, ok := <-in
                        if !ok {
diff --git a/shared/util.go b/shared/util.go
index a71cb91..9e5d5ab 100644
--- a/shared/util.go
+++ b/shared/util.go
@@ -129,16 +129,26 @@ func ReadToJSON(r io.Reader, req interface{}) error {
        return json.Unmarshal(buf, req)
 }
 
-func ReaderToChannel(r io.Reader) <-chan []byte {
+func ReaderToChannel(r io.Reader, bufferSize int) <-chan []byte {
+       if bufferSize <= 128 * 1024 {
+               bufferSize = 128 * 1024
+       }
+
        ch := make(chan ([]byte))
 
        go func() {
+               readSize := 128 * 1024
+               offset := 0
+               buf := make([]byte, bufferSize)
+
                for {
-                       /* io.Copy uses a 32KB buffer, so we might as well too. 
*/
-                       buf := make([]byte, 32*1024)
-                       nr, err := r.Read(buf)
-                       if nr > 0 {
-                               ch <- buf[0:nr]
+                       read := buf[offset:offset+readSize]
+                       nr, err := r.Read(read)
+                       offset += nr
+                       if offset + readSize >= bufferSize || err != nil {
+                               ch <- buf[0:offset]
+                               offset = 0
+                               buf = make([]byte, bufferSize)
                        }
 
                        if err != nil {

From f4ea4b29559561f246eac991312c82dda8bfc350 Mon Sep 17 00:00:00 2001
From: Tycho Andersen <tycho.ander...@canonical.com>
Date: Tue, 24 May 2016 16:06:13 +0000
Subject: [PATCH 2/2] add a test for ReaderToChannel

Signed-off-by: Tycho Andersen <tycho.ander...@canonical.com>
---
 shared/util_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/shared/util_test.go b/shared/util_test.go
index 33e12b7..d5927ea 100644
--- a/shared/util_test.go
+++ b/shared/util_test.go
@@ -1,8 +1,10 @@
 package shared
 
 import (
+       "bytes"
        "fmt"
        "io/ioutil"
+       "math/rand"
        "os"
        "strings"
        "testing"
@@ -100,3 +102,43 @@ func TestReadLastNLines(t *testing.T) {
                }
        }
 }
+
+func TestReaderToChannel(t *testing.T) {
+       buf := make([]byte, 64 * 1024 * 1024)
+       rand.Read(buf)
+
+       offset := 0
+       finished := false
+
+       ch := ReaderToChannel(bytes.NewBuffer(buf), -1)
+       for {
+               data, ok := <-ch
+               if len(data) > 0 {
+                       for i := 0; i < len(data); i++ {
+                               if buf[offset+i] != data[i] {
+                                       t.Error(fmt.Sprintf("byte %d didn't 
match", offset+i))
+                                       return
+                               }
+                       }
+
+                       offset += len(data)
+                       if offset > len(buf) {
+                               t.Error("read too much data")
+                               return
+                       }
+
+                       if offset == len(buf) {
+                               finished = true
+                       }
+               }
+
+               if !ok {
+                       if !finished {
+                               t.Error("connection closed too early")
+                               return
+                       } else {
+                               break
+                       }
+               }
+       }
+}
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to