围棋中的字符计数发生在片 [英] Counting occurrence of character in slice in Go

查看:138
本文介绍了围棋中的字符计数发生在片的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好吧,所以我撞到南墙。

编辑:
使用 bytes.IndexByte()在我的计数()功能使得它几乎运行速度的两倍。 bytes.IndexByte()是用汇编,而不是围棋。仍然不是C的速度,而是更接近。

我有两个方案,一个是C,一个在走那算都在一个文件中换行。超级简单。 C程序中〜1.5秒运行,围棋在〜一个2.4GB的文件4.25秒。

我是不是打去的速度极限?如果是这样,的什么的,准确地说,是导致此?我可以读C,但我看不懂大会这样比较C'S ASM和Go的ASM没有做太多对我来说,除了显示,Go有〜400多行(忽略.ascii部分)。

虽然我知道围棋无法比拟C步骤换步,我不会承担4倍的增长放缓。

想法?

下面是围棋的cpuprofile:

这里是C(编译瓦特/ 的gcc -Wall -pedantic -O9

 的#include<&stdio.h中GT;
#包括LT&;&stdlib.h中GT;
#包括LT&;&stdint.h GT;
#包括LT&;&string.h中GT;
#包括LT&; SYS / types.h中>
#包括LT&; SYS / stat.h>
#包括LT&;&fcntl.h GT;
#包括LT&;&errno.h中GT;的#define BUFFER_SIZE(16 * 1024)INT
主要()
{    为const char *文件=big.txt;
    INT FD =打开(文件,O_RDONLY);
    焦炭BUF [BUFFER_SIZE + 1];
    uintmax_t型字节;
    为size_t bytes_read缓存;
    为size_t线;    posix_fadvise(FD,0,0,POSIX_FADV_SEQUENTIAL);
    而((bytes_read缓存= safe_read(FD,BUF,BUFFER_SIZE))0)
    {
        的char * p = BUF;        //错误检查        而((P =了memchr(P,'\\ n',(BUF + bytes_read缓存) - P)))
          {
            ++磷;
            ++线;
          }
        字节+ = bytes_read缓存;
    }
    的printf(%祖\\ n,字节);
    的printf(%祖\\ n,行);
    返回0;
}

和GO:

 主包进口(
    旗
    FMT
    IO
    OS
    运行/ pprof
    系统调用
)常量(
    POSIX_FADV_SEQUENTIAL = 2    NewLineByte ='\\ n'//或10
    BUFFERSIZE =(16 * 1024)+ 1个
)VAR缓冲=化妆([]字节,缓冲区大小)FUNC fadvise(FILE * os.File,关,长整型,建议UINT32){错误
    _,_,错误号:= syscall.Syscall6(syscall.SYS_FADVISE64,file.Fd(),uintptr(关闭),uintptr(长),uintptr(咨询),0,0)
    如果错误号!= 0 {
        返回错误号
    }
    零回报
}FUNC计数(S []字节)的Int64 {
    计数:=的Int64(0)
    对于i:= 0; I<镜片);我++ {
        如果s [I] == {NewLineByte
            算上++
        }
    }
    返回计数
}FUNC的main(){    文件错误:= os.Open(big.txt)
    如果犯错!= {为零
        恐慌(ERR)
    }    VAR线INT64
    VAR字节的Int64    fadvise(文件,0,0,POSIX_FADV_SEQUENTIAL)
    为{        N,ERR:= file.Read(缓冲)
        如果犯错=零和放大器;!&安培;呃!= {io.EOF
            恐慌(ERR)
        }        线+ =计数(缓冲区[:N])
        字节+ = Int64的(N)        如果犯错== {io.EOF
            打破
        }
    }    fmt.Printf(%d个\\ N个字节)
    fmt.Printf(%d个\\ N,行)
}


解决方案

这里一个不太硬,而不是过慢的方式,使用 bytes.IndexByte (因为你发现了Go的它ASM实施帮助)和 syscall.Mmap

 主包进口(
    字节
    FMT
    日志
    OS
    系统调用
)FUNC的main(){
    如果len(os.Args)LT; 2 {
        log.Fatal(命令行上传递文件名)
    }
    楼ERR:= os.Open(os.Args [1])
    如果犯错!= {为零
        log.Fatal(开放,ERR)
    }
    统计,ERR:= f.Stat()
    如果犯错!= {为零
        log.Fatal(静:ERR)    }
    数据ERR:= syscall.Mmap(INT(f.Fd()),0,INT(stat.Size()),syscall.PROT_READ,syscall.MAP_SHARED)
    如果犯错!= {为零
        log.Fatal(MMAP:ERR)
    }
    换行:= 0
    为{
        I:= bytes.IndexByte(数据,10)
        如果我== -1 {
            打破
        }
        换行++
        数据=数据[I + 1:]
    }
    fmt.Println(新行)
}

MMAP 看起来怪异,但在这里它就像如果你愿意文件读入到片,除了资源密集型由于操作系统的帮助更少。

可以并行计数没有太多的更多的工作,但我不肯定是值得的。 (它不会冲击我,如果在 AMD64 增益为零或负数,如果,例如,单核计数受到内存带宽的限制,但是这还不快给我测试)。

Okay, so I've hit a brick wall.

Edit: Using bytes.IndexByte() in my count() function makes it run almost twice as fast. bytes.IndexByte() is written in assembly instead of Go. Still not C speed, but closer.

I have two programs, one in C and one in Go that both count newlines in a file. Super simple. The C program runs in ~1.5 seconds, the Go in ~4.25 seconds on a 2.4GB file.

Am I hitting Go's speed limit? If so, what, exactly, is causing this? I can read C, but I can't read Assembly so comparing the C's asm and the Go's asm doesn't do much to me except show that the Go has ~400 more lines (ignoring the .ascii section).

While I know Go can't match C step-for-step, I wouldn't assume a 4x slowdown.

Ideas?

Here's the cpuprofile of the Go:

Here's the C (compiled 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;
}

And the 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)
}

解决方案

Here's a not too hard and not too slow way, using bytes.IndexByte (since you found Go's asm implementation of it helped) and 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 looks weird, but here it's much as if you'd read the file into a slice, except less resource-intensive thanks to the OS's help.

You can parallelize the counting without too much more work, but I'm not sure that's worth it. (It would not shock me if the gain on amd64 were zero or negative if, for example, single-core counting were limited by memory bandwidth, but that's not quick for me to test.)

这篇关于围棋中的字符计数发生在片的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆