计算 Go 中切片中字符的出现次数

好的,所以我撞到了一堵砖墙。

编辑:bytes.IndexByte()在我的count()函数中 使用使其运行速度几乎快两倍。bytes.IndexByte()是用汇编而不是 Go 编写的。仍然不是C速度,但更接近。

我有两个程序,一个在 C 中,一个在 Go 中,它们都计算文件中的换行符。超级简单。在 2.4GB 的文件上,C 程序运行约 1.5 秒,Go 运行约 4.25 秒。

我是否达到了 Go 的速度限制?如果是这样,究竟什么导致了这种情况?我能读 C,但我不能读汇编,所以比较 C 的 asm 和 Go 的 asm 对我没有太大作用,只是表明 Go 有大约 400 多行(忽略 .ascii 部分)。

虽然我知道 Go 无法逐步匹配 C,但我不会假设速度会降低 4 倍。

想法?

这是 Go 的 cpuprofile:

http://img3.mukewang.com/613f12ab0001c1b508610623.jpg

这是 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;

}


慕工程0101907
浏览 167回答 2
2回答

森栏

这是一种不太难也不太慢的方法,使用bytes.IndexByte(因为你发现 Go 的 asm 实现有帮助)和syscall.Mmap:package mainimport (&nbsp; &nbsp; "bytes"&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "log"&nbsp; &nbsp; "os"&nbsp; &nbsp; "syscall")func main() {&nbsp; &nbsp; if len(os.Args) < 2 {&nbsp; &nbsp; &nbsp; &nbsp; log.Fatal("pass filename on command line")&nbsp; &nbsp; }&nbsp; &nbsp; f, err := os.Open(os.Args[1])&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Fatal("open: ", err)&nbsp; &nbsp; }&nbsp; &nbsp; stat, err := f.Stat()&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Fatal("stat: ", err)&nbsp; &nbsp; }&nbsp; &nbsp; data, err := syscall.Mmap(int(f.Fd()), 0, int(stat.Size()), syscall.PROT_READ, syscall.MAP_SHARED)&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Fatal("mmap: ", err)&nbsp; &nbsp; }&nbsp; &nbsp; newlines := 0&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; i := bytes.IndexByte(data, 10)&nbsp; &nbsp; &nbsp; &nbsp; if i == -1 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; newlines++&nbsp; &nbsp; &nbsp; &nbsp; data = data[i+1:]&nbsp; &nbsp; }&nbsp; &nbsp; fmt.Println(newlines)}Mmap 看起来很奇怪,但在这里就像您将文件读入一个切片一样,除了由于操作系统的帮助而占用的资源较少。您可以在没有太多工作的情况下并行计数,但我不确定这是否值得。(amd64例如,如果单核计数受到内存带宽的限制,如果增益为零或负值,我不会感到震惊,但这对我来说测试速度并不快。)
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go