Hi there, I recently managed a data parsing C library in a CGO program by for long time network IO.
The data flow is as follows: user socket <----> socketpair[0] <---> socketpair[1] <----> data store |----------Go domain--------------------|----------C domain-------------| With such a data flow, I need to pass a file descriptor to C universe for a long time blocking CGO call (the CGO call returns only if the connection is broken), the CGO call lies in a goroutine. Thus, my first question is: Considering the scheduling strategy of goroutines in Go universe, will the Go universe IO performance between a user socket and socketpair[0] get suffering by such a blocking CGO call? The second question set is regarding IO performance. My benchmark shows the pure Go syscall Write and Read is roughly 15% slower than C system call, and net package IO performance is roughly equal to CGO call performance, as shown as follows: [image: Cgo, Go and C in system call (3).png] Test in go 1.11; Machine: MacBook Pro 2014 Retina; Data: https://docs.google.com/spreadsheets/d/1DwtZmP8fKKr3pOQWVJrD30DSOzv4_qB5KZvsd-DQ1KA/edit?usp=sharing So, questions are, what did I do wrong regarding these benchmarks? I'm currently using syscall.Write() and syscall.Read() approach in different write/read goroutines, is there any way to achieve performance closer to C system call (expect down to 5%) for such a long time network IO? Thank you in advance. Benchmarks: package syscall import ( "net" "os" "syscall" "testing" ) const message = "hello, world!" var buffer = make([]byte, 13) func writeAll(fd int, buf []byte) error { for len(buf) > 0 { n, err := syscall.Write(fd, buf) if n < 0 { return err } buf = buf[n:] } return nil } func BenchmarkReadWriteCgoCalls(b *testing.B) { fds, _ := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0) for i := 0; i < b.N; i++ { CwriteAll(fds[0], []byte(message)) Cread(fds[1], buffer) } } func BenchmarkReadWriteGoCalls(b *testing.B) { fds, _ := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0) for i := 0; i < b.N; i++ { writeAll(fds[0], []byte(message)) syscall.Read(fds[1], buffer) } } func BenchmarkReadWriteNetCalls(b *testing.B) { cs, _ := socketpair() for i := 0; i < b.N; i++ { cs[0].Write([]byte(message)) cs[1].Read(buffer) } } func socketpair() (conns [2]net.Conn, err error) { fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0) if err != nil { return } conns[0], err = fdToFileConn(fds[0]) if err != nil { return } conns[1], err = fdToFileConn(fds[1]) if err != nil { conns[0].Close() return } return } func fdToFileConn(fd int) (net.Conn, error) { f := os.NewFile(uintptr(fd), "") defer f.Close() return net.FileConn(f) } ===================================================== package syscall /* #include <unistd.h> int write_all(int fd, void* buffer, size_t length) { while (length > 0) { int written = write(fd, buffer, length); if (written < 0) return -1; length -= written; buffer += written; } return length; } int read_call(int fd, void *buffer, size_t length) { return read(fd, buffer, length); } */ import "C" import ( "unsafe" ) // CwriteAll is a cgo call for write func CwriteAll(fd int, buf []byte) error { _, err := C.write_all(C.int(fd), unsafe.Pointer(&buf[0]), C.size_t(len(buf))) return err } // Cread is a cgo call for read func Cread(fd int, buf []byte) (int, error) { ret, err := C.read_call(C.int(fd), unsafe.Pointer(&buf[0]), C.size_t(len(buf))) return int(ret), err } C: #include <time.h> #include <stdio.h> #include <unistd.h> #include <sys/socket.h> int write_all(int fd, void* buffer, size_t length) { while (length > 0) { int written = write(fd, buffer, length); if (written < 0) return -1; length -= written; buffer += written; } return length; } int read_call(int fd, void *buffer, size_t length) { return read(fd, buffer, length); } struct timespec timer_start(){ struct timespec start_time; clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start_time); return start_time; } long timer_end(struct timespec start_time){ struct timespec end_time; clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_time); long diffInNanos = (end_time.tv_sec - start_time.tv_sec) * (long)1e9 + (end_time.tv_nsec - start_time.tv_nsec); return diffInNanos; } int main() { int i = 0; int N = 500000; int fds[2]; char message[14] = "hello, world!\0"; char buffer[14] = {0}; socketpair(AF_UNIX, SOCK_STREAM, 0, fds); struct timespec vartime = timer_start(); for(i = 0; i < N; i++) { write_all(fds[0], message, sizeof(message)); read_call(fds[1], buffer, 14); } long time_elapsed_nanos = timer_end(vartime); printf("BenchmarkReadWritePureCCalls\t%d\t%ld ns/op\n", N, time_elapsed_nanos/N); } -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.