-
Notifications
You must be signed in to change notification settings - Fork 169
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
Fix of a null pointer dereference in flip_color #106
Conversation
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.
Thanks for the PR. Did you check running the unit tests with this change and did it pass?
https://github.com/wolkykim/qlibc/blob/master/tests/README.md
Looks like the test all passes. What do you think, we make a separate PR that includes the segfault cases first in the unit test code, then we come back on this PR to address it? Unit test code is here https://github.com/wolkykim/qlibc/blob/master/tests/test_qtreetbl.c |
Sounds good to me, I will look more closely into the random examples which cause segfault and try to come up with an unit test |
Hi, I've created a working branch for this work named
|
I like the step by step representation, that would simplify testing. |
Also, as you can see on the screenshot above, I have found an example when rule 4 violation occurs and it's only 20 elements, however I did that randomly, so I'll look into it and try to find violation with minimum elements. And eventually this violation will cause segfault, however it happens quite far (like several thousands nodes in from my random tests). So imo we might first address rule violation fix, which on its own resolves any future null dereference errors |
We have this functionality already. The debug function(
https://github.com/wolkykim/qlibc/blob/tree/src/containers/qtreetbl.c#L882)
in `tree` branch prints it out that way to support larger elements. (code
in master branch doesn't have this but in `tree` branch). FYI
…On Fri, Jul 21, 2023 at 9:59 AM Stroh Snow ***@***.***> wrote:
What do you think we draw tree like this?
[image: image]
<https://user-images.githubusercontent.com/82967876/255235964-e3c90229-0e7f-4107-9f92-a85c0f8195bc.png>
—
Reply to this email directly, view it on GitHub
<#106 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABUKQMKSAZAJIFIYZPTKNDDXRKYNRANCNFSM6AAAAAA2RTDU5Y>
.
You are receiving this because you commented.Message ID:
***@***.***>
|
I have rewritten the tree printing entirely. It's more close to the drawing you attached above. I think this is better to trace larger trees. Hope this helps. 06cb183#diff-932784f13a92d5db3cc5e6128f9110553be5a0f7a731e06820d74437ad44f5deR848
5 and 8 are Red nodes. |
Great finding. Did you mean you already wrote the drawing code? Yeah i like
it. I thought that was just an idea you wanted. You are welcome to PR that
code.
…On Sat, Jul 22, 2023, 1:03 AM Stroh Snow ***@***.***> wrote:
Yeah, that's not bad, though I can make the branches shorter as well and I
kinda like right angles more. After some trial and error I found what might
be the easiest way to cause rule violation, now I just need to trace it and
see what exact lines of code we are missing. I mean the initial PR commit
probably fixes them all, but we can't just blindly push them without proper
testing.
Here's my first test:
[image: image]
<https://user-images.githubusercontent.com/82967876/255318594-9f7591d8-1e7d-4dca-958b-93978c0071b2.png>
[image: image]
<https://user-images.githubusercontent.com/82967876/255318603-c65f8c40-0154-4b02-8c37-a9bfbbe25966.png>
It also breaks if I delete F and R.
Also I was thinking about recreating tree which causes segfault according
to clang analyzer, but this looks scary lol
[image: image]
<https://user-images.githubusercontent.com/82967876/255318798-2967f209-a0e1-4241-8169-2cd6b2fbe5fb.png>
Probably will do a little bit later (can't really put much time into work
I'm on summer vacation with my family)
—
Reply to this email directly, view it on GitHub
<#106 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABUKQMLLO2RSSFAKU4Z5GDTXROCMBANCNFSM6AAAAAA2RTDU5Y>
.
You are receiving this because you commented.Message ID:
***@***.***>
|
Yeah I just copied it from one my labs I did during the semester :D |
I confirm that rule 4 violation case. That's a great finding. I've added the unit test case for that 37e0c32#diff-f348265e2683d7e4a3cd33f11806d692b0c2e3769a58c0258ce8832a7c56af81R170 I guess we're almost there. I'd like to let you handle the fix from now on unless you don't want to. Thanks! This is great! |
@wolkykim I have finished the fix, check it out. |
@strohsnow I like it a lot. I like every commits you made there including the pretty tree. I just added some minor style adjustments and some more unit testing. This looks awesome!!! BTW, did you happen to find the segfault case? |
Thank you, it's an honour for me to be a part of this project and a pleasure to hear that you like it. |
@strohsnow I'll tell ya the truth. You are a rising star with a very fast-spinning brain. I appreciate your contributions to this project. BTW, about the segfault case, are you talking about this line? https://github.com/wolkykim/qlibc/blob/master/src/containers/qtreetbl.c#L1064 |
@wolkykim Appreciate it, man! |
Oh, nvm. Just removing this check isn't enough for the segfault fix. It just removes clang analyzer warning. The segfault still happens in flip_color, but it's so far away, that analyzer just doesn't have enough depth. So I don't think I will be able to make a test for it. However I couldn't get any segfaults with my fixes in our new branch. |
ok, thanks for the clarification. Oh yeah! Sounds like the segfault case has been likely fixed in the new branch. |
I was wondering though, why did you decide to go with 2-3-4 LLRB instead of 2-3 version? We could have actually fixed all the issues by moving the 4-node split part in the insert function :D |
It's been a while but as I recall 2-3-4 provides a more balanced performance. Depending on the type of workload 2-3 tree has a higher chance of frequent rotations than 2-3-4? I'm open to hearing what your thoughts are. It's been a while since I was digging into this matter. Would it be better to eliminate 4-node splits? What would be the pros/cons? |
@wolkykim |
I've seen this doc before. While it brings good insight and I was enjoying reading it, it seemed to lack supporting data. I haven't done apple-to-apple comparisons by myself. I still think that the algorithm we have is a great choice for a well-balanced generic container but I'm curious, what characteristics of RB or 2-3 variants make you think it would
make it faster? I think reduced rotation doesn't necessarily mean overall performance is better.
|
@wolkykim Insert phase: Normal RB 0.582 rotations/insert, LLRB 1.725 rotations/insert (2.96x—probably a constant factor) Here are some overall performance numbers. Insert phase: conventional RB 0.476s, LLRB 0.560s (1.18x) As you can see it doesn't affect search time, but it does affect both insert and delete operations. If we are talking 2-3 LLRB vs 2-3-4 LLRB, then there is no difference performance wise. Just few small differences in code, you can see code written in Java that implements both 2-3 and 2-3-4 variants here. BTW 2-3 variant doesn't need moveRedLeft fix that is present in the code above. If we are talking 2-3 RB vs 2-3-4 RB, then there is also no difference in performance (2-3 is negligibly slower), however it's easier to implement than 2-3-4 RB. Now that I think about it, we should probably leave it as it is now in our new branch. There is probably a lot of other libs that have RB trees, then there is GoLLRB which is 2-3 variant for anyone curious, and qlibc will remain unique as the only lib that has 2-3-4 variant of LLRB implemented (according to Wikipedia). |
Yeah, I meant RB and 2-3 LLRB.
Is it a well-known fact as of today, RB outperforms LLRB all the ways?
Because the data didn't look quite line up to me.
By looking at the speed of find phase are the same, the costs of finding a key or leaf in insert and delete can be considered the same like about 0.4s Insertion occurs incremental and deletion occurs decremental, so they are in the same N condition
Delete speed in RB is slower than its Insert speed despite it having lesser average rotations. It spent 0.05s for rotation in insert vs 4x times spent in delete.
In LLRB case, insert spent 0.14s in rotation which looks roughly right(3x of 0.05s) but Delete spent only 0.60s where I expect 1.00s (20x of 0.5s) And the measurement only covers the balanced case by using random numbers.
I'm not saying that the paper is false but it didn't give me a trustworthy feel either. I wish the paper shared at least the test code used. But I'm also curious, we have a similar 1 million key tests ready now, we can actually compare them apple-to-apple if you're up for it. This is a good talk.
* TEST : Test tree performance / 1 million keys
Insert 1000000 keys: 345ms
Lookup 1000000 keys: 252ms
Delete 1000000 keys: 393ms
|
Yeah, it's not a well-known fact, we gotta test it by ourselves. I will rewrite qtreetbl using RB trees on a different branch when I have free time and run performance tests. However, I've already ran some tests on both variants of LLRB and results are the same.
2-3 LLRB:
I ran every test 10 times and calculated the average. |
Sounds good to me. I've added a perf tester for a more detailed comparison. Here's a test output on my laptop. It's quite interesting.
|
Some tests were not generating the sample keys as intended. Updated the output above. |
Here are the results, 2-3-4 on the left and 2-3 on the right |
I don't like the amount of color flips in the 2-3 variant, we should stick with 2-3-4. |
This is valuable data. Fantastic. It looks like 2-3-4 makes it a better choice for insert&lookup heavy applications. Is the modified version for the 2-3 model without |
The test for the 2-3 model above uses move-red-left from master branch. You gave me an idea to try and test it with the fix provided in the stackoverfow post, maybe it will change some numbers. I will brb with the test and put the code on the branch as comments. |
2-3 with |
No changes, so I guess it is indeed 2-3-4 exclusive fix |
so I guess the 2-3 variant conversion is basically moving the 4-node splitting to the way up from the way down path in the insert but how is the move-to-left eliminated? Can you post a code diff here? |
@wolkykim
Another altered function is
The last change is in the
I hope the difference is clear now. You can also call me on discord, if you got any more questions. My username is |
Well, well, well. Here are the CLRB tests.
|
Ascending and Descending tests look sweet, however I don't think that the loss in the lookup speed in Random test worth the gain in the deletion speed. It's up to you now if you think we should migrate to CLRB. It's still a lot of work, but I can handle the most of it, I will still need you to write the tests, because you are very good at it imo. |
Hey Strow. This is great. Thanks for the detailed clarification above as well. |
About the migration, I think it's something to think about but for now, let's wrap the new branch up and get it rolled out. If no more work is ongoing then I'll tomorrow evening for the merge. |
I ran some calculations for fun. For the records,
If the calculation is reasonable, we've got a quite different result. |
Oh, don't worry, a pretty table is the least difficult work out there haha. |
I'm not sure this is correct, it's logarithmic, not linear. Two times faster machine, wow, that's something. I am running Ryzen 5 5600U on my laptop. No idea how he got such bad delete results, I literally copy pasted code from the book lol. I wonder if any optimization can be done to the LLRB deletion algorithm in order to cut down the rotations and potentially decrease time. |
I found non-recursive LLRB implementation written in C preprocessor macros in jemalloc. I could try rewrite it and see if it's any faster. I suppose it will eliminate many extra rotations. |
Can't make the deletion work :(
|
All the work and improvements are now merged in. Woohoo!!! I appreciate your hard work and efforts in the tree improvements. I look forward to continuing work and collaboration. A new release cut coming up soon. |
As a quick reference of our test result. Random 1 million keys
|
Yay! I enjoyed working with you. It feels good to actually put my newly acquired (I just finished DSA course this semester) programming skills into the real world projects. |
@strohsnow really great work you've done there. A new release is out. Check it out https://github.com/wolkykim/qlibc/releases/tag/v2.5.0 |
I like it a lot! I will continue working on the llrb branch and try to figure out iterative implementations and test how they perform. |
In accordance with this stackoverflow post I have made some changes to the deletion. Without them rule 4 and rule 5 violations can occur which leads to segfault after dereferencing a null pointer in flip_color function. Along with deletion changes I removed some unnecessary checks for null pointers, except for the ones in remove_obj function, which are obligatory for handling deletion of unexistent keys. After testing on huge sets of random data I don't get any rule violations, segfaults or clang analyzer warnings anymore.