Как эффективно рассчитать контрольную сумму файла - PullRequest
0 голосов
/ 06 января 2020

Я хочу эффективно рассчитать контрольную сумму очень большого файла (несколько ГБ). Эта Go программа имеет два подхода: один разбивает файл на части и вычисляет контрольную сумму quicksha, но это неверно. Другой классический подход slowsha работает хорошо.

Можете ли вы помочь мне исправить quicksha?

package main

import (
    "bufio"
    "crypto/sha256"
    "encoding/hex"
    "io"
    "log"
    "net/http"
    "net/http/pprof"
    "os"
)

func slowsha(fname string) {
    f, err := os.Open(fname)
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()

    h := sha256.New()
    if _, err := io.Copy(h, f); err != nil {
        log.Fatal(err)
    }
    log.Printf("%s %s", hex.EncodeToString(h.Sum(nil)), os.Args[1])
}

func quicksha(fname string) {
    f, err := os.Open(fname)
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()

    buf := make([]byte, 16*1024)
    pr, pw := io.Pipe()

    go func() {
        w := bufio.NewWriter(pw)
        for {
            n, err := f.Read(buf)
            if n > 0 {
                buf = buf[:n]
                w.Write(buf)
            }
            if err == io.EOF {
                pw.Close()
                break
            }
        }
    }()
    h := sha256.New()
    io.Copy(h, pr)
    log.Printf("%s %s", hex.EncodeToString(h.Sum(nil)), os.Args[1])
}

func main() {
    fname := os.Args[2]
    choice := os.Args[1]

    for i := 0; i < 100; i++ {
        if choice == "-s" {
            slowsha(fname)
        } else if choice == "-f" {
            quicksha(fname)
        } else {
            log.Fatal("Bad choice")
        }
    }
}

Вывод

shasum -a 256 lessthan20MBTest.doc >> reference answer
d91b998a372035c2378fc40a6d0eee17b9f16d60207343f9fc3558eb77f90b71  lessthan20MBTest.doc

./quicksha -f lessthan20MBTest.doc >> wrong answer
b97d5167bbe945ca90223b7503653df89ba9e7d420268da27851fca6db3fcdcf lessthan20MBTest.doc

./quicksha -s lessthan20MBTest.doc . >>> right answer
d91b998a372035c2378fc40a6d0eee17b9f16d60207343f9fc3558eb77f90b71  lessthan20MBTest.doc

1 Ответ

3 голосов
/ 06 января 2020

В вашей программе несколько проблем:

Первое: вы уже используете буфер для чтения / записи, поэтому нет необходимости использовать bufio.Writer. Вы двойная буферизация с этим. Это также является причиной того, что вы не получите желаемый результат: вам нужно w.Flush() перед закрытием канала, потому что вы не записали то, что в буферах bufio.Writer, в канал:

if err == io.EOF {
    w.Flush()
    pw.Close()
    break
}

Второе: вы сокращаете буфер. Как правило, read не нужно читать, чтобы заполнить буфер. Если базовый поток является сетевым потоком, read может считывать меньше размера буфера, и это не означает, что достигнут конец потока. Для файлов это не имеет никакого значения на практике, но в целом вы должны сделать следующее:

if n > 0 {
    w.Write(buf[:n])
}

В-третьих: Вы измеряли? Маловероятно, что «более быстрая» реализация на самом деле быстрее. Включая буферизацию в io.Copy, вы реализуете тройную буферизацию в этой реализации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...