Buffered I/O is a technique used to enhance the performance of reading from or writing to streams of data, such as files or network connections, by reducing the number of system calls made. In this chapter, we'll explore buffered I/O in detail, starting from the basics and progressing to more advanced concepts.
Buffered I/O involves temporarily storing data in memory buffers before reading from or writing to a stream. This buffering mechanism helps minimize the overhead associated with frequent system calls, resulting in improved performance.
In this section, we’ll cover the basic concepts of buffered I/O, including how to create buffered readers and writers in Go.
Buffered reading allows us to read data from a stream efficiently by reading chunks of data into memory buffers.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Open("example.txt")
if err != nil {
fmt.Println("Error:", err)
return
}
defer file.Close()
reader := bufio.NewReader(file)
data, err := reader.ReadString('\n')
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("First line of the file:", data)
}
bufio.NewReader()
.ReadString()
method reads the first line of the file into a buffer efficiently.Buffered writing involves writing data to a stream efficiently by buffering it in memory before performing actual write operations.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Create("output.txt")
if err != nil {
fmt.Println("Error:", err)
return
}
defer file.Close()
writer := bufio.NewWriter(file)
_, err = writer.WriteString("Hello, World!")
if err != nil {
fmt.Println("Error:", err)
return
}
writer.Flush() // Ensure all buffered data is written to the underlying file
fmt.Println("Data written to file successfully")
}
bufio.NewWriter()
.WriteString()
method writes the string “Hello, World!” to the buffer efficiently.Flush()
to ensure all buffered data is written to the underlying file.You can specify custom buffer sizes when creating buffered readers and writers to optimize memory usage and performance.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Open("example.txt")
if err != nil {
fmt.Println("Error:", err)
return
}
defer file.Close()
bufferSize := 1024 // Specify custom buffer size
reader := bufio.NewReaderSize(file, bufferSize)
// Read data from the file using the custom buffer size
// ...
}
bufio.NewReaderSize()
Buffered I/O can also be applied to streaming operations, such as reading from a network connection or writing to a web server.
package main
import (
"bufio"
"fmt"
"net/http"
)
func main() {
resp, err := http.Get("https://example.com")
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
reader := bufio.NewReader(resp.Body)
// Read data from the HTTP response body using buffered reading
// ...
}
http.Get()
to make a GET request to “https://example.com” and obtain the response.bufio.NewReader()
to efficiently read data from the response body.Concurrent buffered reading involves reading data from multiple streams concurrently, each with its own buffered reader. This can be useful when dealing with multiple files or network connections simultaneously.
package main
import (
"bufio"
"fmt"
"os"
"sync"
)
func main() {
files := []string{"file1.txt", "file2.txt", "file3.txt"}
var wg sync.WaitGroup
for _, filename := range files {
wg.Add(1)
go func(file string) {
defer wg.Done()
readFile(file)
}(filename)
}
wg.Wait()
}
func readFile(filename string) {
file, err := os.Open(filename)
if err != nil {
fmt.Println("Error:", err)
return
}
defer file.Close()
reader := bufio.NewReader(file)
// Read data from the file using buffered reading
// ...
}
sync.WaitGroup
to wait for all concurrent reading operations to finish.Concurrent buffered writing involves writing data to multiple streams concurrently, each with its own buffered writer. This can be useful for tasks such as logging to multiple files simultaneously
package main
import (
"bufio"
"fmt"
"os"
"sync"
)
func main() {
files := []string{"output1.txt", "output2.txt", "output3.txt"}
var wg sync.WaitGroup
for _, filename := range files {
wg.Add(1)
go func(file string) {
defer wg.Done()
writeFile(file)
}(filename)
}
wg.Wait()
}
func writeFile(filename string) {
file, err := os.Create(filename)
if err != nil {
fmt.Println("Error:", err)
return
}
defer file.Close()
writer := bufio.NewWriter(file)
// Write data to the file using buffered writing
// ...
writer.Flush()
}
sync.WaitGroup
to wait for all concurrent writing operations to finish.In conclusion, concurrent buffered I/O in Go allows for efficient parallel processing of reading from and writing to streams of data. By leveraging Go's concurrency features alongside buffered I/O operations, you can achieve significant performance improvements in handling I/O-intensive tasks. Experiment with different concurrency patterns and buffer sizes to optimize the performance of your applications. However, always remember to handle concurrency safely to avoid race conditions and other concurrency-related issues. Happy coding !❤️