Browse Source

reduce copying of output lines

tags/v0.12.0
Shivaram Lingamneni 6 years ago
parent
commit
3150f4e23b
2 changed files with 29 additions and 14 deletions
  1. 8
    4
      irc/client.go
  2. 21
    10
      irc/socket.go

+ 8
- 4
irc/client.go View File

@@ -820,13 +820,13 @@ func (client *Client) SendRawMessage(message ircmsg.IrcMessage) error {
820 820
 
821 821
 	// assemble message
822 822
 	maxlenTags, maxlenRest := client.maxlens()
823
-	line, err := message.LineMaxLen(maxlenTags, maxlenRest)
823
+	line, err := message.LineMaxLenBytes(maxlenTags, maxlenRest)
824 824
 	if err != nil {
825 825
 		logline := fmt.Sprintf("Error assembling message for sending: %v\n%s", err, debug.Stack())
826 826
 		client.server.logger.Error("internal", logline)
827 827
 
828 828
 		message = ircmsg.MakeMessage(nil, client.server.name, ERR_UNKNOWNERROR, "*", "Error assembling message for sending")
829
-		line, _ := message.Line()
829
+		line, _ := message.LineBytes()
830 830
 
831 831
 		client.socket.Write(line)
832 832
 		return err
@@ -834,10 +834,14 @@ func (client *Client) SendRawMessage(message ircmsg.IrcMessage) error {
834 834
 
835 835
 	// if we used the trailing hack, we need to strip the final space we appended earlier on
836 836
 	if usedTrailingHack {
837
-		line = line[:len(line)-3] + "\r\n"
837
+		copy(line[len(line)-3:], []byte{'\r', '\n'})
838
+		line = line[:len(line)-1]
838 839
 	}
839 840
 
840
-	client.server.logger.Debug("useroutput", client.nick, " ->", strings.TrimRight(line, "\r\n"))
841
+	if client.server.logger.IsLoggingRawIO() {
842
+		logline := string(line[:len(line)-2]) // strip "\r\n"
843
+		client.server.logger.Debug("useroutput", client.nick, " ->", logline)
844
+	}
841 845
 
842 846
 	client.socket.Write(line)
843 847
 

+ 21
- 10
irc/socket.go View File

@@ -34,7 +34,8 @@ type Socket struct {
34 34
 	// this is a trylock enforcing that only one goroutine can write to `conn` at a time
35 35
 	writerSemaphore Semaphore
36 36
 
37
-	buffer        []byte
37
+	buffers       [][]byte
38
+	totalLength   int
38 39
 	closed        bool
39 40
 	sendQExceeded bool
40 41
 	finalData     string // what to send when we die
@@ -121,15 +122,23 @@ func (socket *Socket) Read() (string, error) {
121 122
 // 2. MUST NOT reorder messages
122 123
 // 3. MUST provide mutual exclusion for socket.conn.Write
123 124
 // 4. SHOULD NOT tie up additional goroutines, beyond the one blocked on socket.conn.Write
124
-func (socket *Socket) Write(data string) (err error) {
125
+func (socket *Socket) Write(data []byte) (err error) {
126
+	if len(data) == 0 {
127
+		return
128
+	}
129
+
125 130
 	socket.Lock()
126 131
 	if socket.closed {
127 132
 		err = io.EOF
128
-	} else if len(data)+len(socket.buffer) > socket.maxSendQBytes {
129
-		socket.sendQExceeded = true
130
-		err = errSendQExceeded
131 133
 	} else {
132
-		socket.buffer = append(socket.buffer, data...)
134
+		prospectiveLen := socket.totalLength + len(data)
135
+		if prospectiveLen > socket.maxSendQBytes {
136
+			socket.sendQExceeded = true
137
+			err = errSendQExceeded
138
+		} else {
139
+			socket.buffers = append(socket.buffers, data)
140
+			socket.totalLength = prospectiveLen
141
+		}
133 142
 	}
134 143
 	socket.Unlock()
135 144
 
@@ -165,7 +174,7 @@ func (socket *Socket) readyToWrite() bool {
165 174
 	socket.Lock()
166 175
 	defer socket.Unlock()
167 176
 	// on the first time observing socket.closed, we still have to write socket.finalData
168
-	return !socket.finalized && (len(socket.buffer) > 0 || socket.closed || socket.sendQExceeded)
177
+	return !socket.finalized && (socket.totalLength > 0 || socket.closed || socket.sendQExceeded)
169 178
 }
170 179
 
171 180
 // send actually writes messages to socket.Conn; it may block
@@ -193,11 +202,13 @@ func (socket *Socket) send() {
193 202
 func (socket *Socket) performWrite() {
194 203
 	// retrieve the buffered data, clear the buffer
195 204
 	socket.Lock()
196
-	buffer := socket.buffer
197
-	socket.buffer = nil
205
+	buffers := socket.buffers
206
+	socket.buffers = nil
207
+	socket.totalLength = 0
198 208
 	socket.Unlock()
199 209
 
200
-	_, err := socket.conn.Write(buffer)
210
+	// on Linux, the runtime will optimize this into a single writev(2) call:
211
+	_, err := (*net.Buffers)(&buffers).WriteTo(socket.conn)
201 212
 
202 213
 	socket.Lock()
203 214
 	shouldClose := (err != nil) || socket.closed || socket.sendQExceeded

Loading…
Cancel
Save