-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
[stdlib] Implement Dict.setdefault(key, default)
#2803
Conversation
Signed-off-by: Manuel Saelices <msaelices@gmail.com>
Signed-off-by: Manuel Saelices <msaelices@gmail.com>
Signed-off-by: Manuel Saelices <msaelices@gmail.com>
43bf8d7
to
e81f4de
Compare
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.
Thank you for the pull request! It's a very useful function to have ! I actually discoved it last week and it made my code much simpler. I think we can improve the performance by avoiding copies and working with references whenever possible.
stdlib/src/collections/dict.mojo
Outdated
@@ -855,6 +855,22 @@ struct Dict[K: KeyElement, V: CollectionElement]( | |||
self._index = _DictIndex(self._reserved) | |||
self._entries = Self._new_entries(self._reserved) | |||
|
|||
fn setdefault(inout self, key: K, owned default: V) -> V: |
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.
fn setdefault(inout self, key: K, owned default: V) -> V: | |
fn setdefault(inout self: Reference[Self, _, _], key: K, owned default: V) -> Reference[V, self.is_mutable, self.lifetime]: |
It should be possible to return a reference here to avoid a move. It will make this method work with non-copyable types in the future (and also moar speeeeeed!!!!)
stdlib/src/collections/dict.mojo
Outdated
self[key] = default | ||
return default |
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.
The line self[key] = default
triggers a copy since there is another use of default
afterwards.
What about replacing it with self[key] = default^
?
We can then return self.__get_ref(key)
to work with references :) No copy there.
I think in the future, for even better performance, we should return a reference to the object being inserted in the Dict._insert
function. That can avoid a second lookup. It can be another pull request as it's more complicated.
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 suggestions. Unfortunately, the self[][key] = default^
trick is not working:
❯ ./stdlib/scripts/run-tests.sh
Creating build directory for building the stdlib running the tests in.
Packaging up the Standard Library.
Included from /home/msaelices/src/mojo/stdlib/src/collections/__init__.mojo:1:
/home/msaelices/src/mojo/stdlib/src/collections/dict.mojo:871:19: error: expression must be mutable in assignment
self[][key] = default^
~~~~~~^~~~~
mojo: error: failed to parse the provided Mojo source module
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.
Maybe self
should be mutable for this to work?
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.
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.
maybe fn setdefault(self: Reference[Self, True, _], ...
Also if that's possible, can you avoid using screenshots? If you use copy-paste of text, it's better, as other can copy paste your error messages, and can also search for them from the search bar of github.
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.
I followed your suggestions in this commit, even if it's failing:
msaelices@7b3844f
I'm waiting for your expertise @gabrieldemarmiesse . Thanks in advance.
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.
I tested locally and this is the right fix. It works. Notice the True
inside the Reference
. Also you need to remove the inout
EDIT: Locally in the unit tests, I had to dereference the result with []
, which is expected
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.
See gabrieldemarmiesse@1144383 for the fix
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 so much. I've just merged it in this PR: msaelices#1
@@ -524,10 +524,21 @@ fn test_clear() raises: | |||
assert_equal(len(some_dict), 0) | |||
|
|||
|
|||
fn test_dict_setdefault() raises: |
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.
Can we add another test that verifies that no copies are happening when calling setdefault
? We can use the CopyCounter
struct for this purpose. There is an example here: https://github.com/modularml/mojo/blob/nightly/stdlib/test/collections/test_list.mojo#L568
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.
Will do it when I manage to fix the compiler issue above. Thank you!
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.
There is still this new test to add
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.
Done here: msaelices@7e53b09
It's curious. I thought the case of the "b" key would have 0 copies but it's 1.
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.
Maybe you should use the transfer operator in the test ^
? Maybe that's why there is a copy. Also I think calling __getitem__
triggers a copy
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.
I tried it but no luck: msaelices@91d9ee5
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.
It may be a bug in the dict implementation. let's skip this problem for now. I think it needs to be investigated later on, but that would be out of scope for this PR.
Also can you rename your pull request? By putting |
Dict.setdefault(key, default)
Dict.setdefault(key, default)
It will not copy while returning the value Suggested by @gabrieldemarmiesse Signed-off-by: Manuel Saelices <msaelices@gmail.com>
Signed-off-by: gabrieldemarmiesse <gabrieldemarmiesse@gmail.com>
Fix implementation
Signed-off-by: Manuel Saelices <msaelices@gmail.com>
@gabrieldemarmiesse I've merged your fix, and also synced the Thanks so much. |
Signed-off-by: Manuel Saelices <msaelices@gmail.com>
stdlib/src/collections/dict.mojo
Outdated
self[][key] = default^ | ||
return self[].__get_ref(key) |
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.
self[][key] = default^ | |
return self[].__get_ref(key) | |
self[][key] = default^ | |
return self[].__get_ref(key) |
There is no need to put it outside the except, no? Is it because it fails at compile time otherwise?
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.
No special reason. Fixed here: msaelices@ab36941
Signed-off-by: Manuel Saelices <msaelices@gmail.com>
Signed-off-by: Manuel Saelices <msaelices@gmail.com>
Signed-off-by: Manuel Saelices <msaelices@gmail.com>
Signed-off-by: Manuel Saelices <msaelices@gmail.com>
@gabrieldemarmiesse could you please take another look? I've synced from |
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.
Looks good to me! There is still the problem with the copy to be investiguated, but I believe it's unrelated to your PR so we can skip it for now. At least it's documented in the unit tests :)
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.
Looks good to me! There is still the problem with the copy to be investiguated, but I believe it's unrelated to your PR so we can skip it for now. At least it's documented in the unit tests :)
Signed-off-by: Connor Gray <code@connorgray.com>
!sync |
✅🟣 This contribution has been merged 🟣✅ Your pull request has been merged to the internal upstream Mojo sources. It will be reflected here in the Mojo repository on the nightly branch during the next Mojo nightly release, typically within the next 24-48 hours. We use Copybara to merge external contributions, click here to learn more. |
[External] [stdlib] Implement `Dict.setdefault(key, default)` Implement `Dict.setdefault` which returns a reference with the value of the item with the specified key. If the key does not exist, it is inserted with the specified value into the `Dict`. Co-authored-by: Manuel Saelices <msaelices@gmail.com> Closes #2803 MODULAR_ORIG_COMMIT_REV_ID: a5514f823c3b7dc978607435d8082f1efe14aa13
Landed in a842307! Thank you for your contribution 🎉 |
[External] [stdlib] Implement `Dict.setdefault(key, default)` Implement `Dict.setdefault` which returns a reference with the value of the item with the specified key. If the key does not exist, it is inserted with the specified value into the `Dict`. Co-authored-by: Manuel Saelices <msaelices@gmail.com> Closes #2803 MODULAR_ORIG_COMMIT_REV_ID: a5514f823c3b7dc978607435d8082f1efe14aa13
Dict.setdefault(key, default)
Dict.setdefault(key, default)