为什么在逐行读取文件时,缓冲区大小始终是4096的整数倍?

时间:2014-07-06 15:00:07

标签: go

示例代码是,

// test.go
package main

import (
    "bufio"
    "os"
)

func main() {
    if len(os.Args) != 2 {
        println("Usage:", os.Args[0], "")
        os.Exit(1)
    }
    fileName := os.Args[1]
    fp, err := os.Open(fileName)
    if err != nil {
        println(err.Error())
        os.Exit(2)
    }
    defer fp.Close()
    r := bufio.NewScanner(fp)
    var lines []string
    for r.Scan() {
        lines = append(lines, r.Text())
    }
}

c:\> go build test.go

c:\> test.exe test.txt

然后我在执行时使用进程监视器监视其进程,部分输出是:

test.exe  ReadFile  SUCCESS      Offset: 4,692,375, Length: 8,056
test.exe  ReadFile  SUCCESS      Offset: 4,700,431, Length: 7,198
test.exe  ReadFile  SUCCESS      Offset: 4,707,629, Length: 8,134
test.exe  ReadFile  SUCCESS      Offset: 4,715,763, Length: 7,361
test.exe  ReadFile  SUCCESS      Offset: 4,723,124, Length: 8,056
test.exe  ReadFile  SUCCESS      Offset: 4,731,180, Length: 4,322
test.exe  ReadFile  END OF FILE  Offset: 4,735,502, Length: 8,192

等效的java代码是,

//Test.java
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;

public class Test{
public static void main(String[] args) {
  try
  {
  FileInputStream in = new FileInputStream("test.txt");
  BufferedReader br = new BufferedReader(new InputStreamReader(in));
  String strLine;
  while((strLine = br.readLine())!= null)
  {
   ;
  }
  }catch(Exception e){
   System.out.println(e);
  }
 }
}

c:\> javac Test.java

c:\> java Test

然后部分监控输出是:

java.exe  ReadFile  SUCCESS       Offset: 4,694,016, Length: 8,192
java.exe  ReadFile  SUCCESS       Offset: 4,702,208, Length: 8,192
java.exe  ReadFile  SUCCESS       Offset: 4,710,400, Length: 8,192
java.exe  ReadFile  SUCCESS       Offset: 4,718,592, Length: 8,192
java.exe  ReadFile  SUCCESS       Offset: 4,726,784, Length: 8,192
java.exe  ReadFile  SUCCESS       Offset: 4,734,976, Length: 526
java.exe  ReadFile  END OF FILE   Offset: 4,735,502, Length: 8,192

如您所见,java中的缓冲区大小为8192,每次读取8192个字节。为什么每次读取文件时,Go in Go都会发生变化?

我尝试了bufio.ReadString('\n')bufio.ReadBytes('\n'),但他们都遇到了同样的问题。

[更新] 我用C测试了样本,

//test.c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
        FILE * fp;
        char * line = NULL;
        size_t len = 0;
        ssize_t read;
        fp = fopen("test.txt", "r");
        if (fp == NULL)
                exit(EXIT_FAILURE);
        while ((read = getline(&line, &len, fp)) != -1) {
                printf("Retrieved line of length %zu :\n", read);
        }
        if (line)
                free(line);
        return EXIT_SUCCESS;
}

输出类似于java代码(我的系统上缓冲区大小为65536)。那么为什么Go在这里有如此不同?

2 个答案:

答案 0 :(得分:2)

阅读bufio.Scan&#39; source显示,当缓冲区大小为4096时,它会根据多少&#34;空&#34;空间留在其中,特别是这部分:

n, err := s.r.Read(s.buf[s.end:len(s.buf)])

现在性能明智,我几乎肯定你所使用的任何文件系统都足够聪明,可以预读和缓存数据,因此缓冲区大小不应该大大增加差。

答案 1 :(得分:1)

可能是的原因:

在您引用的所有示例中,Scan函数输出由行结尾确定。

Go的默认扫描功能按行分割(http://golang.org/pkg/bufio/#Scanner.Scan):

  

默认的拆分功能将输入分为线路终端剥离的行

由于bufio.ReadString('\n')字符,bufio.ReadBytes('\n')\n存在同样的问题。

尝试从测试文件中删除所有换行符并测试它是否仍在READFILE条记录中提供非4096倍。

正如一些人所建议的,你所看到的实际上可能是由于bufio包使用的IO策略。