summaryrefslogtreecommitdiff
path: root/src/io
diff options
context:
space:
mode:
authorAlbert Nigmatzianov <albertnigma@gmail.com>2017-08-31 16:00:37 +0500
committerIan Lance Taylor <iant@golang.org>2017-09-20 13:41:50 +0000
commit098eb01600fe0e90aee21d204924c67188fe26d4 (patch)
tree5eb4a0200c102649204cce9de6243ef4bcfcd602 /src/io
parent977578816ea7d0c2b5e00e612e222a8378abf11e (diff)
downloadgo-git-098eb01600fe0e90aee21d204924c67188fe26d4.tar.gz
io: Improve performance of CopyN
Benchmarks: name old time/op new time/op delta CopyNSmall-4 5.09µs ± 1% 2.25µs ±86% -55.91% (p=0.000 n=11+14) CopyNLarge-4 114µs ±73% 121µs ±72% ~ (p=0.701 n=14+14) name old alloc/op new alloc/op delta CopyNSmall-4 34.6kB ± 0% 1.9kB ±19% -94.60% (p=0.000 n=12+14) CopyNLarge-4 129kB ± 8% 127kB ±18% -2.00% (p=0.007 n=14+14) name old allocs/op new allocs/op delta CopyNSmall-4 2.00 ± 0% 1.00 ± 0% -50.00% (p=0.000 n=14+14) CopyNLarge-4 2.00 ± 0% 1.00 ± 0% -50.00% (p=0.000 n=14+14) Benchmark code: type Buffer struct { bytes.Buffer io.ReaderFrom } func BenchmarkCopyNSmall(b *testing.B) { bs := bytes.Repeat([]byte{0}, 1024) rd := bytes.NewReader(bs) buf := new(Buffer) b.ResetTimer() for i := 0; i < b.N; i++ { io.CopyN(buf, rd, 512) rd.Reset(bs) } } func BenchmarkCopyNLarge(b *testing.B) { bs := bytes.Repeat([]byte{0}, 64*1024) rd := bytes.NewReader(bs) buf := new(Buffer) b.ResetTimer() for i := 0; i < b.N; i++ { io.CopyN(buf, rd, (32*1024)+1) rd.Reset(bs) } } Change-Id: Id8d29e55758452c870cf372db640f07baec05849 Reviewed-on: https://go-review.googlesource.com/60630 Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
Diffstat (limited to 'src/io')
-rw-r--r--src/io/io.go51
1 files changed, 50 insertions, 1 deletions
diff --git a/src/io/io.go b/src/io/io.go
index 28dab08e46..86710ed6f3 100644
--- a/src/io/io.go
+++ b/src/io/io.go
@@ -335,7 +335,7 @@ func ReadFull(r Reader, buf []byte) (n int, err error) {
// If dst implements the ReaderFrom interface,
// the copy is implemented using it.
func CopyN(dst Writer, src Reader, n int64) (written int64, err error) {
- written, err = Copy(dst, LimitReader(src, n))
+ written, err = copyN(dst, src, n)
if written == n {
return n, nil
}
@@ -346,6 +346,55 @@ func CopyN(dst Writer, src Reader, n int64) (written int64, err error) {
return
}
+// copyN copies n bytes (or until an error) from src to dst.
+// It returns the number of bytes copied and the earliest
+// error encountered while copying.
+//
+// If dst implements the ReaderFrom interface,
+// the copy is implemented using it.
+func copyN(dst Writer, src Reader, n int64) (int64, error) {
+ // If the writer has a ReadFrom method, use it to do the copy.
+ if rt, ok := dst.(ReaderFrom); ok {
+ return rt.ReadFrom(LimitReader(src, n))
+ }
+
+ l := 32 * 1024 // same size as in copyBuffer
+ if n < int64(l) {
+ l = int(n)
+ }
+ buf := make([]byte, l)
+
+ var written int64
+ for n > 0 {
+ if n < int64(len(buf)) {
+ buf = buf[:n]
+ }
+
+ nr, errR := src.Read(buf)
+ if nr > 0 {
+ n -= int64(nr)
+ nw, errW := dst.Write(buf[:nr])
+ if nw > 0 {
+ written += int64(nw)
+ }
+ if errW != nil {
+ return written, errW
+ }
+ if nr != nw {
+ return written, ErrShortWrite
+ }
+ }
+
+ if errR != nil {
+ if errR != EOF {
+ return written, errR
+ }
+ return written, nil
+ }
+ }
+ return written, nil
+}
+
// Copy copies from src to dst until either EOF is reached
// on src or an error occurs. It returns the number of bytes
// copied and the first error encountered while copying, if any.