Eliminated memory allocations from client's DoTimeout and GetTimeout

This commit is contained in:
Aliaksandr Valialkin
2015-11-26 17:04:27 +02:00
parent bc85e2b572
commit a3fd75d237
2 changed files with 55 additions and 17 deletions
+23 -15
View File
@@ -468,14 +468,19 @@ func clientGetURLTimeout(dst []byte, url string, timeout time.Duration, c client
}
}
type clientURLResponse struct {
statusCode int
body []byte
err error
}
func clientGetURLTimeoutFreeConn(dst []byte, url string, timeout time.Duration, c clientDoer) (statusCode int, body []byte, err error) {
var ch chan error
var ch chan clientURLResponse
chv := errorChPool.Get()
if chv == nil {
ch = make(chan error, 1)
} else {
ch = chv.(chan error)
chv = make(chan clientURLResponse, 1)
}
ch = chv.(chan clientURLResponse)
req := acquireRequest()
@@ -486,29 +491,32 @@ func clientGetURLTimeoutFreeConn(dst []byte, url string, timeout time.Duration,
// Without this 'hack' the load on slow host could exceed MaxConns*
// concurrent requests, since timed out requests on client side
// usually continue execution on the host.
var statusCodeCopy int
var bodyCopy []byte
go func() {
var errCopy error
statusCodeCopy, bodyCopy, errCopy = doRequest(req, dst, url, c)
ch <- errCopy
statusCodeCopy, bodyCopy, errCopy := doRequest(req, dst, url, c)
ch <- clientURLResponse{
statusCode: statusCodeCopy,
body: bodyCopy,
err: errCopy,
}
}()
var tc *time.Timer
tcv := timerPool.Get()
if tcv == nil {
tc = time.NewTimer(timeout)
tcv = tc
} else {
tc = tcv.(*time.Timer)
initTimer(tc, timeout)
}
select {
case err = <-ch:
case resp := <-ch:
releaseRequest(req)
errorChPool.Put(chv)
statusCode = statusCodeCopy
body = bodyCopy
statusCode = resp.statusCode
body = resp.body
err = resp.err
case <-tc.C:
body = dst
err = ErrTimeout
@@ -625,10 +633,9 @@ func clientDoTimeoutFreeConn(req *Request, resp *Response, timeout time.Duration
var ch chan error
chv := errorChPool.Get()
if chv == nil {
ch = make(chan error, 1)
} else {
ch = chv.(chan error)
chv = make(chan error, 1)
}
ch = chv.(chan error)
// Make req and resp copies, since on timeout they no longer
// may be accessed.
@@ -651,6 +658,7 @@ func clientDoTimeoutFreeConn(req *Request, resp *Response, timeout time.Duration
tcv := timerPool.Get()
if tcv == nil {
tc = time.NewTimer(timeout)
tcv = tc
} else {
tc = tcv.(*time.Timer)
initTimer(tc, timeout)
+32 -2
View File
@@ -69,7 +69,37 @@ func acquireFakeServerConn(s []byte) *fakeClientConn {
var fakeClientConnPool sync.Pool
func BenchmarkClientGetFastServer(b *testing.B) {
func BenchmarkClientGetTimeoutFastServer(b *testing.B) {
body := []byte("123456789099")
s := []byte(fmt.Sprintf("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %d\r\n\r\n%s", len(body), body))
c := &Client{
Dial: func(addr string) (net.Conn, error) {
return acquireFakeServerConn(s), nil
},
}
nn := uint32(0)
b.RunParallel(func(pb *testing.PB) {
url := fmt.Sprintf("http://foobar%d.com/aaa/bbb", atomic.AddUint32(&nn, 1))
var statusCode int
var bodyBuf []byte
var err error
for pb.Next() {
statusCode, bodyBuf, err = c.GetTimeout(bodyBuf[:0], url, time.Second)
if err != nil {
b.Fatalf("unexpected error: %s", err)
}
if statusCode != StatusOK {
b.Fatalf("unexpected status code: %d", statusCode)
}
if !bytes.Equal(bodyBuf, body) {
b.Fatalf("unexpected response body: %q. Expected %q", bodyBuf, body)
}
}
})
}
func BenchmarkClientDoFastServer(b *testing.B) {
body := []byte("012345678912")
s := []byte(fmt.Sprintf("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %d\r\n\r\n%s", len(body), body))
c := &Client{
@@ -97,7 +127,7 @@ func BenchmarkClientGetFastServer(b *testing.B) {
})
}
func BenchmarkNetHTTPClientGetFastServer(b *testing.B) {
func BenchmarkNetHTTPClientDoFastServer(b *testing.B) {
body := []byte("012345678912")
s := []byte(fmt.Sprintf("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %d\r\n\r\n%s", len(body), body))
c := &http.Client{