reduce copying of output lines

This commit is contained in:
Shivaram Lingamneni 2018-04-26 15:32:32 -04:00
parent 5b416a0922
commit 3150f4e23b
2 changed files with 29 additions and 14 deletions

View File

@ -820,13 +820,13 @@ func (client *Client) SendRawMessage(message ircmsg.IrcMessage) error {
// assemble message // assemble message
maxlenTags, maxlenRest := client.maxlens() maxlenTags, maxlenRest := client.maxlens()
line, err := message.LineMaxLen(maxlenTags, maxlenRest) line, err := message.LineMaxLenBytes(maxlenTags, maxlenRest)
if err != nil { if err != nil {
logline := fmt.Sprintf("Error assembling message for sending: %v\n%s", err, debug.Stack()) logline := fmt.Sprintf("Error assembling message for sending: %v\n%s", err, debug.Stack())
client.server.logger.Error("internal", logline) client.server.logger.Error("internal", logline)
message = ircmsg.MakeMessage(nil, client.server.name, ERR_UNKNOWNERROR, "*", "Error assembling message for sending") message = ircmsg.MakeMessage(nil, client.server.name, ERR_UNKNOWNERROR, "*", "Error assembling message for sending")
line, _ := message.Line() line, _ := message.LineBytes()
client.socket.Write(line) client.socket.Write(line)
return err return err
@ -834,10 +834,14 @@ func (client *Client) SendRawMessage(message ircmsg.IrcMessage) error {
// if we used the trailing hack, we need to strip the final space we appended earlier on // if we used the trailing hack, we need to strip the final space we appended earlier on
if usedTrailingHack { if usedTrailingHack {
line = line[:len(line)-3] + "\r\n" copy(line[len(line)-3:], []byte{'\r', '\n'})
line = line[:len(line)-1]
} }
client.server.logger.Debug("useroutput", client.nick, " ->", strings.TrimRight(line, "\r\n")) if client.server.logger.IsLoggingRawIO() {
logline := string(line[:len(line)-2]) // strip "\r\n"
client.server.logger.Debug("useroutput", client.nick, " ->", logline)
}
client.socket.Write(line) client.socket.Write(line)

View File

@ -34,7 +34,8 @@ type Socket struct {
// this is a trylock enforcing that only one goroutine can write to `conn` at a time // this is a trylock enforcing that only one goroutine can write to `conn` at a time
writerSemaphore Semaphore writerSemaphore Semaphore
buffer []byte buffers [][]byte
totalLength int
closed bool closed bool
sendQExceeded bool sendQExceeded bool
finalData string // what to send when we die finalData string // what to send when we die
@ -121,15 +122,23 @@ func (socket *Socket) Read() (string, error) {
// 2. MUST NOT reorder messages // 2. MUST NOT reorder messages
// 3. MUST provide mutual exclusion for socket.conn.Write // 3. MUST provide mutual exclusion for socket.conn.Write
// 4. SHOULD NOT tie up additional goroutines, beyond the one blocked on socket.conn.Write // 4. SHOULD NOT tie up additional goroutines, beyond the one blocked on socket.conn.Write
func (socket *Socket) Write(data string) (err error) { func (socket *Socket) Write(data []byte) (err error) {
if len(data) == 0 {
return
}
socket.Lock() socket.Lock()
if socket.closed { if socket.closed {
err = io.EOF err = io.EOF
} else if len(data)+len(socket.buffer) > socket.maxSendQBytes {
socket.sendQExceeded = true
err = errSendQExceeded
} else { } else {
socket.buffer = append(socket.buffer, data...) prospectiveLen := socket.totalLength + len(data)
if prospectiveLen > socket.maxSendQBytes {
socket.sendQExceeded = true
err = errSendQExceeded
} else {
socket.buffers = append(socket.buffers, data)
socket.totalLength = prospectiveLen
}
} }
socket.Unlock() socket.Unlock()
@ -165,7 +174,7 @@ func (socket *Socket) readyToWrite() bool {
socket.Lock() socket.Lock()
defer socket.Unlock() defer socket.Unlock()
// on the first time observing socket.closed, we still have to write socket.finalData // on the first time observing socket.closed, we still have to write socket.finalData
return !socket.finalized && (len(socket.buffer) > 0 || socket.closed || socket.sendQExceeded) return !socket.finalized && (socket.totalLength > 0 || socket.closed || socket.sendQExceeded)
} }
// send actually writes messages to socket.Conn; it may block // send actually writes messages to socket.Conn; it may block
@ -193,11 +202,13 @@ func (socket *Socket) send() {
func (socket *Socket) performWrite() { func (socket *Socket) performWrite() {
// retrieve the buffered data, clear the buffer // retrieve the buffered data, clear the buffer
socket.Lock() socket.Lock()
buffer := socket.buffer buffers := socket.buffers
socket.buffer = nil socket.buffers = nil
socket.totalLength = 0
socket.Unlock() socket.Unlock()
_, err := socket.conn.Write(buffer) // on Linux, the runtime will optimize this into a single writev(2) call:
_, err := (*net.Buffers)(&buffers).WriteTo(socket.conn)
socket.Lock() socket.Lock()
shouldClose := (err != nil) || socket.closed || socket.sendQExceeded shouldClose := (err != nil) || socket.closed || socket.sendQExceeded