Skip to content

rpl: Sort list of parents#7468

Merged
cgundogan merged 1 commit intoRIOT-OS:masterfrom
bergzand:rpl/parent-sort
Aug 23, 2017
Merged

rpl: Sort list of parents#7468
cgundogan merged 1 commit intoRIOT-OS:masterfrom
bergzand:rpl/parent-sort

Conversation

@bergzand
Copy link
Copy Markdown
Member

This PR enhances the RPL code to sort the list of parents instead of only comparing whether a parent is better than the current preferred parent.

The aim of this PR is to make it easier for objective functions to determine the parent set.
If the objective function defines the which_parent function such that the list is sorted based on how good (best rank or whatever the OF defines) the parent list should start automatically with the preferred parent, folowed by the rest of the parent set.

Since the parent set is needed for rank calculation, the calc_rank function can then iterate over the parent set, and assume that the first N parents from the parent list is his parent set, where N is the parent set. If it encounters an unacceptable parent within the first N parent, it can assume that the following parents are also unacceptable.

The advantage is that the calc_rank function only has to iterate over the parent list and not have to determine which parents are in the parent set.

This requires a small API change in the RPL objective function. The which_parent function needs to return an integer in the same way as the strcmp function [-1, 0, 1], based on which parent is better.

@bergzand bergzand mentioned this pull request Aug 10, 2017
6 tasks
LL_FOREACH(dodag->parents, elt) {
new_best = dodag->instance->of->which_parent(new_best, elt);
}
LL_SORT(dodag->parents, dodag->instance->of->which_parent);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this does not update new_best anymore, I would guess that also changes the behaviour of this function, or not?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ah, you're right there, that wasn't supposed to happen. An new_best = dodag->parents after the LL_SORT should be sufficient to restore the old behaviour

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

yes, I think that should do

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

however, I generally like your suggestion/idea of having the parent list sorted right away. But then this should be done on add/del of a parent so there would be no need to this here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, but for example with MRHOF as an objective function, the sorting of the parents can vary over time because it depends not only on the advertised rank of a parent, but also on the link quality to the parent.

So sorting the parents on add/del works if the parents have a fixed order, but I don't think it works when the relative sorting of the parents change over time.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

good point, then it should be done when ever the rank of a parent changes - what could be quite often and would introduce lots of overhead, which we don't want on an embedded node.

OK, I see this is not as easy as I first thought 😉

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I would not advise to update this every time the path cost of a parent updates, that could be with every transmitted packet (or other event on which the path cost depends).

Recalculating this based on a timer or an event with some build in hysterisis (only update when the path cost changed by some amount) might be enough to lessen the burden of this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

yes, or only when required, like in this function.

uint16_t ocp; /**< objective code point */
uint16_t (*calc_rank)(gnrc_rpl_parent_t *parent, uint16_t base_rank); /**< calculate the rank */
gnrc_rpl_parent_t *(*which_parent)(gnrc_rpl_parent_t *, gnrc_rpl_parent_t *); /**< compare for parents */
int (*which_parent)(gnrc_rpl_parent_t *, gnrc_rpl_parent_t *); /**< compare for parents */
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

instead of changing an existing functions (hence the API) wouldn't suffice to add a separate (helper) function, i.e. int parent_cmp(a, b) - which then could also be used by which_parent btw.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

yes, will change.

@bergzand
Copy link
Copy Markdown
Member Author

@smlng I've fixed your comments. I still want to run some additional tests to check if it behaves as intended. Is the API change/addition this way as you requested?

LL_FOREACH(dodag->parents, elt) {
new_best = dodag->instance->of->which_parent(new_best, elt);
}
LL_SORT(dodag->parents, dodag->instance->of->parent_cmp);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

due to this sort the following is obsolete:

LL_DELETE(dodag->parents, new_best);
LL_PREPEND(dodag->parents, new_best);

below lines 320, 321

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ah, should have noticed that, will remove.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Removed

@smlng smlng requested a review from cgundogan August 17, 2017 14:09
@smlng smlng added Type: enhancement The issue suggests enhanceable parts / The PR enhances parts of the codebase / documentation Area: network Area: Networking CI: needs squashing Commits in this PR need to be squashed; If set, CI systems will mark this PR as unmergable CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Aug 17, 2017
@smlng
Copy link
Copy Markdown
Member

smlng commented Aug 17, 2017

though it seems like which_parent is obsolete now, I would leave it as is for now. IMHO the new name parent_cmp is more appropriate anyway.

@bergzand
Copy link
Copy Markdown
Member Author

Yes, I noticed earlier that not all functions in that struct are (still) in use. The parent_state_callback also seems obsolete.

@smlng
Copy link
Copy Markdown
Member

smlng commented Aug 17, 2017

looks good, please squash

@cgundogan can you have a quick look at this, too?

@smlng
Copy link
Copy Markdown
Member

smlng commented Aug 17, 2017

not all functions in that struct are (still) in use

yeah, I think the RPL code needs some general cleanup - let's do this all at once later on in a separate PR.

@bergzand
Copy link
Copy Markdown
Member Author

squashed

@smlng smlng added CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR and removed CI: needs squashing Commits in this PR need to be squashed; If set, CI systems will mark this PR as unmergable CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Aug 17, 2017
{
if (p1->rank <= p2->rank) {
return p1;
if(parent_cmp(p1, p2) == 1) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would recommend to use if(parent_cmp(p1, p2) > 1) instead of strict equality here, this leaves more freedom on the implementation of parent_cmp. For instance, it might be that parent_cmp returns the actual rank difference. Quoting from the man memcmp:

The memcmp() function returns zero if the two strings are identical, otherwise returns the difference between the first two differing bytes (treated as unsigned char values, so that \200' is greater than \0', for example). Zero-length strings are always identical. This behavior is not required by C and portable code
should only depend on the sign of the returned value.

the last part is what I'm referring to.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Can't argue against that :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

oops > 1 should be > 0, my bad

@bergzand
Copy link
Copy Markdown
Member Author

Remark is fixed. Do you want it squashed again, assuming no further remarks?

{
if (p1->rank <= p2->rank) {
return p1;
if (parent_cmp(p1, p2)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I need to insist on if (parent_cmp(p1, p2) > 0) { because -1 will also eval as true, only 0 is false

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, my bad.

@smlng
Copy link
Copy Markdown
Member

smlng commented Aug 18, 2017

please squash directly

@bergzand
Copy link
Copy Markdown
Member Author

With this comparison fixed, I can reduce the parent_cmp() of of0.c to:
return p2->rank - p1->rank;
(right?)

@smlng
Copy link
Copy Markdown
Member

smlng commented Aug 18, 2017

return p2->rank - p1->rank;

I would be careful with that, because rank is of type uint16_t and parent_cmpreturns int, for which the size depends on the platform used, i.e. the result might not fit. I think its safer to use '-1', '0' and '1' for now. But also make no assumption on that when using parent_cmp in which_parent.

@bergzand
Copy link
Copy Markdown
Member Author

Okay, that makes sense. Thanks for the comments so far.

@smlng
Copy link
Copy Markdown
Member

smlng commented Aug 18, 2017

I'd like to get an opinion on this by @cgundogan or @BytesGalore before merge, they the RPL gurus of RIOT 🤓

uint16_t (*calc_rank)(gnrc_rpl_parent_t *parent, uint16_t base_rank); /**< calculate the rank */
gnrc_rpl_parent_t *(*which_parent)(gnrc_rpl_parent_t *, gnrc_rpl_parent_t *); /**< compare for parents */
gnrc_rpl_parent_t *(*which_parent)(gnrc_rpl_parent_t *, gnrc_rpl_parent_t *); /**< retrieve the better parent */
int (*parent_cmp)(gnrc_rpl_parent_t *, gnrc_rpl_parent_t *); /**< compare for parents */
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I know it hasn't been done before for the other function pointers in this file, but can you add – somehow – a documentation on how to interpret the return value of this function pointer? Is it possible to document function pointers in structs as if they were normal functinos with @param, @return etc.? Would be awesome.

@smlng
Copy link
Copy Markdown
Member

smlng commented Aug 21, 2017

Is it possible to document function pointers in structs as if they were normal functinos with @param, @return etc.? Would be awesome.

yes thats possible, and done in other places already, e.g. #7370 here. But I also think, enhancing the documentation is a separate PR.

@cgundogan
Copy link
Copy Markdown
Member

IMO the documentation for this function is as important as the function itself, at least. Separating them is a bit too liberal-minded for me (:

@smlng
Copy link
Copy Markdown
Member

smlng commented Aug 21, 2017

my point was not against documentation, but rather that documenting just the new function will look strange too, while documenting all functions (even the unrelated once) would bloat this PR.

@cgundogan
Copy link
Copy Markdown
Member

but rather that documenting just the new function will look strange to

To the contrary, IMO providing a documentation just for this new function makes the commit diff more consistent on its own. The documentation for other functions can still be applied by a separate commit or PR. Although, I would not object if they also happen to be in this PR (:

@smlng
Copy link
Copy Markdown
Member

smlng commented Aug 21, 2017

okay then @bergzand please add proper doc for parent_cmp as requested/suggested by @cgundogan. Regarding the other functions: some of them are unused (which_dodag) or obsolete (which_parent) anyway, so as already said: there is room for cleanup RPL, too.

@bergzand
Copy link
Copy Markdown
Member Author

That's fine with me, I should have time tomorrow to do the documentation and maybe even some cleanup.

@bergzand bergzand force-pushed the rpl/parent-sort branch 3 times, most recently from 6494769 to 047ad11 Compare August 22, 2017 08:41
@bergzand
Copy link
Copy Markdown
Member Author

Documentation added. I couldn't get doxygen to understand unnamed function parameters, so they now have names.

* @param[in] parent1 First parent to compare.
* @param[in] parent2 Second parent to compare.
*
* @return Zero if the parents are of equal preference,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

please make each case a separate @return, i.e., for <0, 0, and >0.

*
* This function is used to determine the parent list order. The parents
* are ordered from the preferred parent to the least preferred parent.
* This function should compare two parents based on the criteria of the
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

the last sentence is rather vague (should?), be explicit: Compares two parents based on the rank calculated by the objective function. Btw. this sentence should be the first, followed by the other explaining the (possible) usage.

* positive result if the second parent is prefered,
* negative result if the first parent is prefered.
*/
int (*parent_cmp)(gnrc_rpl_parent_t *parent1, gnrc_rpl_parent_t *parent2);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

IMHO parameter names p1 and p2 were fine, too - and match with actual implementation in of0.c below.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I don't mind changing it back to p1, p2. In my opinion this is a bit more clear and I rather change the implementation in of0 to match this

@smlng smlng added this to the Release 2017.10 milestone Aug 22, 2017
@bergzand
Copy link
Copy Markdown
Member Author

@smlng I've resolved most of your comments. I'd rather have the first sentence of the explanation without mentioning rank because an objective function can have different criteria than rank comparison (metric containers).

Do you have any preference for parent# vs p# for the parent_cmp? I can change either the declaration or the implementation to match the other.

@smlng
Copy link
Copy Markdown
Member

smlng commented Aug 22, 2017

without mentioning rank because an objective function can have different criteria than rank comparison (metric containers).

I don't understand, IIRC RPL uses rank only to choose its parent, hence we compare parents by rank. You are right, that an OF may have different/multiple metrics to choose from which to derive the rank. However, in the end RPL needs a rank which way ever that is calculated. Using OF0 the rank is relative to hop-count while using MRHOF it would be relative to ETX, for instance - still both (hop-count or ETX) are converted to a rank, or not.

Do you have any preference for parent# vs p# for the parent_cmp?

just a (non-blocking) personal preference by me, I'm fine with naming as is - your choice 😉

@bergzand
Copy link
Copy Markdown
Member Author

bergzand commented Aug 22, 2017

I looked it up and I have to agree with you. I've changed the sentence to your suggestion.

@smlng
Copy link
Copy Markdown
Member

smlng commented Aug 22, 2017

@cgundogan fine for you, too; ready to merge?

@cgundogan
Copy link
Copy Markdown
Member

@smlng @bergzand looks good as it is now. ACK and GO

@cgundogan cgundogan merged commit 5c4000c into RIOT-OS:master Aug 23, 2017
@bergzand bergzand deleted the rpl/parent-sort branch September 29, 2017 09:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: network Area: Networking CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Type: enhancement The issue suggests enhanceable parts / The PR enhances parts of the codebase / documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants