Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reduce number of allocations in ScanForLinks #24

Merged
merged 2 commits into from
Jul 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions testing/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import (
"reflect"
"testing"
"testing/quick"

"github.com/ipfs/go-cid"
cbg "github.com/whyrusleeping/cbor-gen"
)

func BenchmarkMarshaling(b *testing.B) {
Expand Down Expand Up @@ -51,3 +54,27 @@ func BenchmarkUnmarshaling(b *testing.B) {
}

}

func BenchmarkLinkScan(b *testing.B) {
r := rand.New(rand.NewSource(123456))
val, ok := quick.Value(reflect.TypeOf(SimpleTypeTwo{}), r)
if !ok {
b.Fatal("failed to construct type")
}

tt := val.Interface().(SimpleTypeTwo)

buf := new(bytes.Buffer)
if err := tt.MarshalCBOR(buf); err != nil {
b.Fatal(err)
}

b.ReportAllocs()
b.ReportAllocs()

for i := 0; i < b.N; i++ {
if err := cbg.ScanForLinks(bytes.NewReader(buf.Bytes()), func(cid.Cid) {}); err != nil {
b.Fatal(err)
}
}
}
40 changes: 25 additions & 15 deletions utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,15 @@ import (
cid "github.com/ipfs/go-cid"
)

func ScanForLinks(br io.Reader) ([]cid.Cid, error) {
var out []cid.Cid
if err := scanForLinksRec(br, func(c cid.Cid) {
out = append(out, c)
}); err != nil {
return nil, err
}
const maxCidLength = 100

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this apply to embedded identity multihash links?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ianopolous yes, though only if youre using this method. Is that an issue? I guess we can make it a var instead of a const so you can tweak it in your own applications

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We only interact over the http api. So hopefully that doesn't affect us then? I can't think of a use case where it actually matters unless pinning could end up calling this to get links and then error?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this library is used in go-ipfs indirectly through go-ipld-cbor: https://github.com/ipfs/go-ipld-cbor/blob/master/go.mod#L12


return out, nil
func ScanForLinks(br io.Reader, cb func(cid.Cid)) error {
buf := make([]byte, maxCidLength)
return scanForLinksRec(br, cb, buf)
}

func scanForLinksRec(br io.Reader, cb func(cid.Cid)) error {
maj, extra, err := CborReadHeader(br)
func scanForLinksRec(br io.Reader, cb func(cid.Cid), scratch []byte) error {
maj, extra, err := CborReadHeaderBuf(br, scratch)
if err != nil {
return err
}
Expand All @@ -39,29 +35,43 @@ func scanForLinksRec(br io.Reader, cb func(cid.Cid)) error {
}
case MajTag:
if extra == 42 {
buf, err := ReadByteArray(br, 1000)
maj, extra, err = CborReadHeaderBuf(br, scratch)
if err != nil {
return err
}
c, err := cid.Cast(buf[1:])

if maj != MajByteString {
return fmt.Errorf("expected cbor type 'byte string' in input")
}

if extra > maxCidLength {
return fmt.Errorf("string in cbor input too long")
}

if _, err := io.ReadAtLeast(br, scratch, int(extra)); err != nil {
return err
}

c, err := cid.Cast(scratch[1:extra])
if err != nil {
return err
}
cb(c)

} else {
if err := scanForLinksRec(br, cb); err != nil {
if err := scanForLinksRec(br, cb, scratch); err != nil {
return err
}
}
case MajArray:
for i := 0; i < int(extra); i++ {
if err := scanForLinksRec(br, cb); err != nil {
if err := scanForLinksRec(br, cb, scratch); err != nil {
return err
}
}
case MajMap:
for i := 0; i < int(extra*2); i++ {
if err := scanForLinksRec(br, cb); err != nil {
if err := scanForLinksRec(br, cb, scratch); err != nil {
return err
}
}
Expand Down