| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Easy-peasy string deduplication to Golang. You can implement this in literally 2 minutes. But you might not want to - please read all of this.
Instanciate a deduplication object by providing a function that takes a []byte slice and returns a hash. As stringdedup is using generics, your returned value type is user specified. For small amounts of strings an uint32 is fine, but you can use uint64 or even a [4]uint64 for a sha256 value. Choose something that is fast ;)
dedup := stringdedup.New(func(in []byte) uint32 {
return xxhash.Checksum32(in)
})
Every time you encounter a string you want deduplicated, just wrap it in a deduplication call:
deduppedstring := dedup.S(inputstring)
You can also ingest []byte and get a deduplicated string back. This saves an allocation per call, se more detailed example below:
inputdata := []byte{0x01, 0x02, 0x03, 0x04}
deduppedstring := dedup.BS(inputdata)
In a scenario where you read a lot of data containing repeated freeform strings, unless you do something special, you're wasting a lot of memory. A very simplistic example could be that you are indexing a lot of files - see the example folder in the package.
I use it in two different projects, and one of them gets a deduplication ratio of 1:5, saving a massive amount of memory.
The example included shows that things are not black and white. You might gain something by using this package, and you might not. It really depends on what you are doing, and also how you are doing it.
Yes, actually Go is quite clever about strings. Or at least as clever as technically possible, without it costing way too much CPU during normal scenarios.
Internally in Golang a string is defined as:
type string struct { // no, you can't do this in reality - use reflect.StringHeader
p pointer // the bytes the string consists of, Golang internally uses "pointer" as the type, this is not a type reachable by mortals
len int // how many bytes are allocated
}
So the string variable takes up 12 bytes of space + the actual space the backing data use + some overhead for the heap management (?). This package tries to cut down on the duplicate backing data when your program introduces the same contents several times.
For the below explainations assume this is defined:
var a, b string a = "ho ho ho"
Every time you read data from somewhere external, run a string through a function (uppercase, lowercase etc), or convert from []byte to string, you allocate new backing data on the heap.
Get the package:
go get github.com/lkarlslund/stringdedup
dedup := stringdedup.New(func(in []byte) uint32 {
return xxhash.Checksum32(in)
})
dedupedstring := dedup.S(somestring) // input string, get deduplicated string back
That's it! You're now guaranteed that this string only exists once in your program, if all the other string allocations process the same way.
If you're repeatedly reading from the same []byte buffer, you can save an allocation per call this way:
dedup := stringdedup.New(func(in []byte) uint32 {
return xxhash.Checksum32(in)
})
buffer := make([]byte, 16384)
var mystrings []string
var err error
for err == nil {
_, err = myreader.Read(buffer)
// do some processing, oh you found something you want to save at buffer[42:103]
mystrings = append(mystrings, dedup.BS(buffer[42:103])) // BS = input []byte, get deduplicated string back
}
If you know that you're not going to dedup any of the existing strings in memory again, you can call:
stringdedup.Flush()
This frees the hashing indexes from stringdedup. It doesn't mean you can not dedup again, it just means that stringdedup forgets about the strings that are already in memory.
This package uses some tricks, that may break at any time, if the Golang developers choose to implement something differently. Namely it's using these particularities:
This requires Go 1.18 on x86 / x64. Please let me know your experiences.
Twitter: @lkarlslund
| Back | FazBrowse Home | New Git URL |