-
Notifications
You must be signed in to change notification settings - Fork 502
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
exp/orderbook: Speed up path finding by changing edgeSet data structure #3965
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,48 +9,67 @@ import ( | |
// edgeSet maintains a mapping of strings (asset keys) to a set of venues, which | ||
// is composed of a sorted lists of offers and, optionally, a liquidity pool. | ||
// The offers are sorted by ascending price (in terms of the buying asset). | ||
type edgeSet map[string]Venues | ||
type edgeSet []edge | ||
|
||
type edge struct { | ||
key string | ||
value Venues | ||
} | ||
|
||
func (e edgeSet) find(key string) int { | ||
for i := 0; i < len(e); i++ { | ||
if e[i].key == key { | ||
return i | ||
} | ||
} | ||
return -1 | ||
} | ||
|
||
// addOffer will insert the given offer into the edge set | ||
func (e edgeSet) addOffer(key string, offer xdr.OfferEntry) { | ||
func (e edgeSet) addOffer(key string, offer xdr.OfferEntry) edgeSet { | ||
// The list of offers in a venue is sorted by cheapest to most expensive | ||
// price to convert buyingAsset to sellingAsset | ||
venues := e[key] | ||
if len(venues.offers) == 0 { | ||
e[key] = Venues{ | ||
offers: []xdr.OfferEntry{offer}, | ||
pool: venues.pool, | ||
} | ||
return | ||
i := e.find(key) | ||
if i < 0 { | ||
return append(e, edge{key: key, value: Venues{offers: []xdr.OfferEntry{offer}}}) | ||
} | ||
|
||
offers := e[i].value.offers | ||
// find the smallest i such that Price of offers[i] > Price of offer | ||
insertIndex := sort.Search(len(venues.offers), func(i int) bool { | ||
return offer.Price.Cheaper(venues.offers[i].Price) | ||
insertIndex := sort.Search(len(offers), func(j int) bool { | ||
return offer.Price.Cheaper(offers[j].Price) | ||
}) | ||
|
||
// then insert it into the slice (taken from Method 2 at | ||
// https://github.com/golang/go/wiki/SliceTricks#insert). | ||
offers := append(venues.offers, xdr.OfferEntry{}) // add to end | ||
offers = append(offers, xdr.OfferEntry{}) // add to end | ||
copy(offers[insertIndex+1:], offers[insertIndex:]) // shift right by 1 | ||
offers[insertIndex] = offer // insert | ||
|
||
e[key] = Venues{offers: offers, pool: venues.pool} | ||
e[i].value = Venues{offers: offers, pool: e[i].value.pool} | ||
return e | ||
} | ||
|
||
// addPool makes `pool` a viable venue at `key`. | ||
func (e edgeSet) addPool(key string, pool xdr.LiquidityPoolEntry) { | ||
venues := e[key] | ||
venues.pool = pool | ||
e[key] = venues | ||
func (e edgeSet) addPool(key string, pool xdr.LiquidityPoolEntry) edgeSet { | ||
i := e.find(key) | ||
if i < 0 { | ||
return append(e, edge{key: key, value: Venues{pool: pool}}) | ||
} | ||
e[i].value.pool = pool | ||
return e | ||
} | ||
|
||
// removeOffer will delete the given offer from the edge set, returning whether | ||
// or not the given offer was actually found. | ||
func (e edgeSet) removeOffer(key string, offerID xdr.Int64) bool { | ||
venues := e[key] | ||
offers := venues.offers | ||
func (e edgeSet) removeOffer(key string, offerID xdr.Int64) (edgeSet, bool) { | ||
i := e.find(key) | ||
if i < 0 { | ||
return e, false | ||
} | ||
|
||
offers := e[i].value.offers | ||
updatedOffers := offers | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we modify There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't want to modify There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah good point, I see. It won't keep iterating after it's modified since it |
||
contains := false | ||
for i, offer := range offers { | ||
if offer.OfferId != offerID { | ||
|
@@ -59,29 +78,32 @@ func (e edgeSet) removeOffer(key string, offerID xdr.Int64) bool { | |
|
||
// remove the entry in the slice at this location (taken from | ||
// https://github.com/golang/go/wiki/SliceTricks#cut). | ||
offers = append(offers[:i], offers[i+1:]...) | ||
updatedOffers = append(offers[:i], offers[i+1:]...) | ||
contains = true | ||
break | ||
} | ||
|
||
if !contains { | ||
return false | ||
return e, false | ||
} | ||
|
||
if len(offers) == 0 && venues.pool.Body.ConstantProduct == nil { | ||
delete(e, key) | ||
} else { | ||
venues.offers = offers | ||
e[key] = venues | ||
if len(updatedOffers) == 0 && e[i].value.pool.Body.ConstantProduct == nil { | ||
return append(e[:i], e[i+1:]...), true | ||
} | ||
|
||
return true | ||
e[i].value.offers = updatedOffers | ||
return e, true | ||
} | ||
|
||
func (e edgeSet) removePool(key string) { | ||
e[key] = Venues{offers: e[key].offers} | ||
} | ||
func (e edgeSet) removePool(key string) edgeSet { | ||
i := e.find(key) | ||
if i < 0 { | ||
return e | ||
} | ||
|
||
if len(e[i].value.offers) == 0 { | ||
return append(e[:i], e[i+1:]...) | ||
} | ||
|
||
func (e edgeSet) isEmpty(key string) bool { | ||
return len(e[key].offers) == 0 && e[key].pool.Body.ConstantProduct == nil | ||
e[i].value = Venues{offers: e[i].value.offers} | ||
return e | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clever, great idea 👍