好的,所以我碰到了一堵砖墙。
修改
在我的bytes.IndexByte()
函数中使用count()
使其运行速度几乎快两倍。 bytes.IndexByte()
是用汇编而不是Go编写的。仍然不是C速度,但更接近。
我有两个程序,一个在C中,一个在Go中,它们都计算文件中的换行符。超级简单。 C程序在大约1.5秒内运行,在2.4GB文件上运行~4.25秒。
我是否达到Go的速度限制?如果是这样,究竟是什么导致了这个?我可以阅读C,但是我无法读取汇编,所以比较C的asm和Go的asm对我没什么用,除了表明Go还有大约400行(忽略.ascii部分)。
虽然我知道Go不能与C步进匹配,但我不会假设4x减速。
想法?
以下是Go的cpuprofile:
这是C(编译为w gcc -Wall -pedantic -O9
)
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define BUFFER_SIZE (16 * 1024)
int
main()
{
const char *file = "big.txt";
int fd = open (file, O_RDONLY);
char buf[BUFFER_SIZE + 1];
uintmax_t bytes;
size_t bytes_read;
size_t lines;
posix_fadvise (fd, 0, 0, POSIX_FADV_SEQUENTIAL);
while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
{
char *p = buf;
// error checking
while ((p = memchr (p, '\n', (buf + bytes_read) - p)))
{
++p;
++lines;
}
bytes += bytes_read;
}
printf("%zu\n", bytes);
printf("%zu\n", lines);
return 0;
}
Go:
package main
import (
"flag"
"fmt"
"io"
"os"
"runtime/pprof"
"syscall"
)
const (
POSIX_FADV_SEQUENTIAL = 2
NewLineByte = '\n' // or 10
BufferSize = (16 * 1024) + 1
)
var Buffer = make([]byte, BufferSize)
func fadvise(file *os.File, off, length int, advice uint32) error {
_, _, errno := syscall.Syscall6(syscall.SYS_FADVISE64, file.Fd(), uintptr(off), uintptr(length), uintptr(advice), 0, 0)
if errno != 0 {
return errno
}
return nil
}
func count(s []byte) int64 {
count := int64(0)
for i := 0; i < len(s); i++ {
if s[i] == NewLineByte {
count++
}
}
return count
}
func main() {
file, err := os.Open("big.txt")
if err != nil {
panic(err)
}
var lines int64
var bytes int64
fadvise(file, 0, 0, POSIX_FADV_SEQUENTIAL)
for {
n, err := file.Read(Buffer)
if err != nil && err != io.EOF {
panic(err)
}
lines += count(Buffer[:n])
bytes += int64(n)
if err == io.EOF {
break
}
}
fmt.Printf("%d\n", bytes)
fmt.Printf("%d\n", lines)
}
答案 0 :(得分:1)
这是关于在文件中计算'\n'
,这个代码在Zorin VM(VMWare播放器),6 GB RAM,4个核心(&amp;)上以~1.26秒(并且大多数更快)运行。电源插入;因为电源管理器有时会阻止CPU过快消耗电池),主机操作系统是Windows 8.我在一些现实世界的项目中使用Go不到6个月,我也是Linux noob。但是我认为问题是从Go调用C并且比纯Go慢得多 - 我在调用一些C代码时遇到过这种情况,dll
并且编译为{{1} }。
cgo
答案 1 :(得分:1)
Here's使用bytes.IndexByte
(因为你发现Go的asm实现有帮助)和syscall.Mmap
:
package main
import (
"bytes"
"fmt"
"log"
"os"
"syscall"
)
func main() {
if len(os.Args) < 2 {
log.Fatal("pass filename on command line")
}
f, err := os.Open(os.Args[1])
if err != nil {
log.Fatal("open: ", err)
}
stat, err := f.Stat()
if err != nil {
log.Fatal("stat: ", err)
}
data, err := syscall.Mmap(int(f.Fd()), 0, int(stat.Size()), syscall.PROT_READ, syscall.MAP_SHARED)
if err != nil {
log.Fatal("mmap: ", err)
}
newlines := 0
for {
i := bytes.IndexByte(data, 10)
if i == -1 {
break
}
newlines++
data = data[i+1:]
}
fmt.Println(newlines)
}
Mmap
看起来很奇怪,但在这里它就好像你将文件读入片中一样,除非由于操作系统的帮助而资源消耗较少。
You can parallelize the counting没有太多工作,但我不确定这是值得的。 (如果amd64
的增益为零或为负,例如单核计数受内存带宽限制,但我不能快速测试,那就不会让我感到震惊。)