홈>
80 바이트의 데이터가 있고 마지막 4 바이트 만 지속적으로 변한다고 가정하면 Go를 사용하여 총 80 바이트를 어떻게 효율적으로 해시 할 수 있습니까? 본질적으로 처음 76 바이트는 동일하지만 마지막 4 바이트는 계속 변경됩니다. 이상적으로는 처음 76 바이트에 대한 해시 다이제스트 사본을 유지하고 마지막 4 바이트 만 계속 변경하려고합니다.
- 답변 # 1
80 바이트의 데이터가 있고 마지막 4 바이트 만 지속적으로 변한다고 가정하면 Go를 사용하여 총 80 바이트를 어떻게 효율적으로 해시 할 수 있습니까? 본질적으로 처음 76 바이트는 동일하지만 마지막 4 바이트는 계속 변경됩니다. 이상적으로는 처음 76 바이트에 대한 해시 다이제스트 사본을 유지하고 마지막 4 바이트 만 계속 변경하려고합니다.
Go Playground에서 다음 예제를 시도 할 수 있습니다. 벤치 마크 결과가 끝났습니다.
참고 : 아래 구현은 동시 사용에 안전하지 않습니다. 나는 의도적으로 이것을 더 간단하고빠르게만들었습니다.
공용 API 만 사용할 때 가장 빠름 (항상 모든 입력을 해시 함) Go 해시 알고리즘의 일반적인 개념과 인터페이스는hash.Hash
입니다. 인터페이스. 이것은 당신이 hasher의 상태를 저장하고 저장된 상태로 돌아가거나 되 감는 것을 허용하지 않습니다. 따라서 Go 표준 라이브러리의 공개 해시 API를 사용하면 항상 시작부터 해시를 계산해야합니다. 공개 API가 제공하는 것은Hash.Reset()
를 사용하여 이미 생성 된 hasher를 재사용하여 새 입력의 해시를 계산하는 것입니다. 방법. 여러 해시 값을 계산하는 데 (메모리) 할당이 필요하지 않기 때문에 좋습니다. 또한Hash.Sum()
로 전달 될 수있는 선택적 슬라이스를 활용할 수 있습니다. 현재 해시를 추가하는 데 사용됩니다. 해시 결과를 받기 위해 할당이 필요하지 않도록하는 것이 좋습니다.다음은 이들을 활용하는 예입니다.
테스트 데이터다음 테스트 데이터를 사용합니다 :
먼저 확실한 결과를 얻자 (우리의 hasher가 제대로 작동하는지 확인하기 위해) :
출력 :
이제
Cached1
를 확인해 봅시다 와셔 :출력은 동일합니다 :
더 빠르지 만 깨질 수 있음 (향후 Go 릴리스에서) : 마지막 4 바이트 만 해시이제 첫 76 개의 고정 부분의 해시를 한 번만 계산하는 덜 유연한 솔루션을 보자.
와이즈 비즈의 허셔 패키지는 수출되지 않은
crypto/sha256
입니다 type (보다 정확하게이 형식에 대한 포인터) :와이즈 비즈의 가치 struct type은 기본적으로 hasher의 현재 상태를 유지합니다.
우리가 할 수있는 일은 고정자에게 처음 76 바이트를 공급 한 다음이 구조체 값을저장하는 것입니다. 첫 76 개가 동일한 80 바이트 데이터의 해시를 계산해야 할 때이 저장된 값을 시작점으로 사용하고 마지막 4 바이트를 바꿉니다.
이 구조체 값은 포인터와 슬라이스 및 맵과 같은 디스크립터 유형을 포함하지 않으므로 간단히 저장하면 충분합니다. 그렇지 않으면 우리는 그것들의 사본도 만들어야하지만 우리는 "행운"입니다. 따라서 향후
// digest represents the partial evaluation of a checksum. type digest struct { h [8]uint32 x [chunk]byte nx int len uint64 is224 bool // mark if this digest is SHA-224 }
를 구현할 경우이 솔루션을 조정해야합니다. 예를 들어 포인터 또는 슬라이스 필드를 추가합니다.와이즈 비즈 이후 내 보내지 않은 경우 반사 만 사용할 수 있습니다 (
digest
기본적으로 계산에 약간의 지연이 발생합니다.이 작업을 수행하는 구현 예 :
테스트 :
출력은 다시 동일합니다 :
그래서 작동합니다.
"궁극적 인"빠른 솔루션type Cached2 struct { origv reflect.Value hasherv reflect.Value hasher hash.Hash result [sha256.Size]byte } func NewCached2(fixed []byte) *Cached2 { h := sha256.New() h.Write(fixed) c := &Cached2{origv: reflect.ValueOf(h).Elem()} hasherv := reflect.New(c.origv.Type()) c.hasher = hasherv.Interface().(hash.Hash) c.hasherv = hasherv.Elem() return c } func (c *Cached2) Sum(data []byte) []byte { // Set state of the fixed hash: c.hasherv.Set(c.origv) c.hasher.Write(data) return c.hasher.Sum(c.result[:0]) }
반사가 발생하지 않으면 더 빨라질 수 있습니다. 더 빠른 솔루션을 원한다면 간단히var c2 = NewCached2(fixed) fmt.Printf("%x\n", c2.Sum(variantA)) fmt.Printf("%x\n", c2.Sum(variantB))
의 사본을 만들 수 있습니다. 유형과 메소드를 패키지에 포함하므로 리플렉션에 의존하지 않고 직접 사용할 수 있습니다.이 작업을 수행하면
fb8e69bdfa2ad15be7cc8a346b74e773d059f96cfc92da89e631895422fe966a 10ef52823dad5d1212e8ac83b54c001bfb9a03dc0c7c3c83246fb988aa788c0c
에 액세스 할 수 있습니다 struct value를 사용하면 다음과 같이 간단하게 사본을 만들 수 있습니다.복원은 다음과 같습니다 :
난 그냥
digest
를 "복제"패키지를 내 작업 공간에 넣고var d digest // init d saved := d
를 변경/내보냈습니다.d = saved
로 입력 데모 목적으로 만 사용하십시오. 그런 다음이crypto/sha256
를 사용하여 내가digest
를 구현 한 유형 이렇게 :테스트 :
다시 출력은 동일합니다. 그래서 이것도 작동합니다.
벤치 마크이 코드로 성능을 벤치마킹 할 수 있습니다 :
벤치 마크 결과 (
type Cached3 struct { orig mysha256.Digest result [sha256.Size]byte } func NewCached3(fixed []byte) *Cached3 { var d mysha256.Digest d.Reset() d.Write(fixed) return &Cached3{orig: d} } func (c *Cached3) Sum(data []byte) []byte { // Make a copy of the fixed hash: d := c.orig d.Write(data) return d.Sum(c.result[:0]) }
) :func BenchmarkCached1(b *testing.B) { for i := 0; i < b.N; i++ { c1.Sum(data) c1.Sum(data2) } } func BenchmarkCached2(b *testing.B) { for i := 0; i < b.N; i++ { c2.Sum(variantA) c2.Sum(variantB) } } func BenchmarkCached3(b *testing.B) { for i := 0; i < b.N; i++ { c3.Sum(variantA) c3.Sum(variantB) } }
go test -bench . -benchmem
보다 약41 % 빠릅니다 꽤 눈에 띄고 훌륭합니다. 와이즈 비즈BenchmarkCached1-4 1000000 1569 ns/op 0 B/op 0 allocs/op BenchmarkCached2-4 2000000 926 ns/op 0 B/op 0 allocs/op BenchmarkCached3-4 2000000 872 ns/op 0 B/op 0 allocs/op
에 비해 "작은"성능 향상 만 제공 또 다른6 %입니다. 와이즈 비즈Cached2
보다44 %빠릅니다. .또한 어떤 솔루션도 할당을 사용하지 않습니다.
결론추가 40 % 또는 44 %에 대해서는 아마도
Cached1
에 가지 않을 것입니다 또는Cached3
솔루션. 물론 실제로 성능이 얼마나 중요한지에 달려 있습니다. 그것이 중요하다면, 나는Cached2
생각 솔루션은 최소한의 복잡성 추가와 눈에 띄는 성능 향상 사이에서 훌륭한 절충안을 제시합니다. 향후 Go 구현으로 인해 문제가 발생할 수 있으므로 위협이됩니다. 문제가 있으면Cached3
현재 구현을 복사하여이 문제를 해결하고 성능을 약간 향상시킵니다.Cached1