-
Notifications
You must be signed in to change notification settings - Fork 125
Add some list-related NIFs #1795
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
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.
First question here: did you notice a relevant performance increase by replacing these functions with a native implementation?
Can you leave some information about this here (and maybe it might be a good idea to add this to the commit message, so we can have some further context about why this change has been done).
One style comment: let's use shorter commit subject / PR title (as encouraged in general with git).
lists:keyfind
, lists:keymember
, lists:keysearch
, lists:member
and erlang:list_to_bitstring
NIFs
Hi @bettio, generally our goal is to make AtomVM more compatible with OTP, and these functions are extensively used in OTP, but implemented in the BEAM as NIFs. Thus, I think it's worthwhile to have them as NIFs regardless of whether the performance improvement is significant. However, I did a simple benchmark with haystack = Enum.to_list(1..200_000) ++ [:needle]
t = :erlang.system_time(:millisecond)
true = :lists.member(:needle, haystack)
true = :lists.member(:needle, haystack)
true = :lists.member(:needle, haystack)
true = :lists.member(:needle, haystack)
true = :lists.member(:needle, haystack)
:erlang.display(:erlang.system_time(:millisecond) - t)
:ok and on my machine the benchmarked part takes around 40 ms before these changes, and 25 ms after, so it's quite big improvement IMO |
Indeed BEAM has at least lists:member/2 and lists:keymember/3 as NIFs as OTP28. For what it's worth, I ran the benchmark on jit branch: 52ms with JIT and 97ms without. The longest part definitely was generating the haystack. |
@pguyot I wasn't aware you're working on JIT, that's great news! Generating the haystack takes very long indeed, not sure why, but I only measured the 'lists:member` part. BTW, keyfind and keysearch are NIFs too. |
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 left some important comments, there are few issues. Still most important: CI must be green.
src/libAtomVM/nifs.c
Outdated
term_list_length(list, &proper); | ||
if (UNLIKELY(!proper)) { | ||
RAISE_ERROR(BADARG_ATOM); | ||
} |
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.
We can remove this part, so it can be just O(1)
: instead of using term_list_length
to check if it is a proper list or not, we can check it at the end.
So we can also be equivalent to the OTP implementation:
iex(3)> :lists.member(3, [1, 2, 3 | :foo])
true
iex(4)> :lists.member(4, [1, 2, 3 | :foo])
** (ArgumentError) errors were found at the given arguments:
* 2nd argument: not a proper list
(stdlib 6.2) :lists.member(4, [1, 2, 3 | :foo])
iex:4: (file)
src/libAtomVM/nifs.c
Outdated
RAISE_ERROR(BADARG_ATOM); | ||
} | ||
|
||
while (!term_is_nil(list)) { |
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.
while (term_is_nonempty_list(list))
src/libAtomVM/nifs.c
Outdated
RAISE_ERROR(BADARG_ATOM); | ||
} | ||
|
||
int n_pos = term_to_int(n); |
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.
nitpicking: avm_int_t
is the right type for storing term_to_int(n)
result.
src/libAtomVM/nifs.c
Outdated
} | ||
|
||
int proper; | ||
term_list_length(tuple_list, &proper); |
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.
same thing as I mentioned earlier.
src/libAtomVM/nifs.c
Outdated
return FALSE_ATOM; | ||
} | ||
|
||
static term nif_lists_keysearch(Context *ctx, int argc, term argv[]) |
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.
This function is deprecated, I'm not sure it is worth it providing a C implementation.
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 main reason to add these functions is for compatibility, so I think it should be there 🤔
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.
Caller do not care if a function is provided using a NIF. Whether a function is implemented as a NIF or not is totally transparent to the caller.
For this specific reason it is not an argument if the BEAM has a certain function implemented as a NIF or not.
Reasons for writing a NIF are:
- performances
- access to native/operating systeam features
So, TL;DR I don't think performance really matters for deprecated functions (except specific situations), so it can be implemented in Erlang, and honestly less C code we have, more I feel happy.
src/libAtomVM/defaultatoms.def
Outdated
X(MONITORED_BY_ATOM, "\xC", "monitored_by") | ||
|
||
X(KEY_ATOM, "\x3", "key") | ||
X(VALUE_ATOM, "\x5", "value") |
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.
Beware the new line at the end of file.
9b384c5
to
5f129fe
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.
There are still a few minor quick fixes that are required, 2 of them are required for green CI:
- erlfmt (let's format all erl file we changed)
- reuse (let's add proper copyright headers)
As soon everything is fixed feel free to squash/fixup everything into a single commit and I will merge it.
src/libAtomVM/nifs.c
Outdated
return FALSE_ATOM; | ||
} | ||
|
||
static term nif_lists_keysearch(Context *ctx, int argc, term argv[]) |
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.
Caller do not care if a function is provided using a NIF. Whether a function is implemented as a NIF or not is totally transparent to the caller.
For this specific reason it is not an argument if the BEAM has a certain function implemented as a NIF or not.
Reasons for writing a NIF are:
- performances
- access to native/operating systeam features
So, TL;DR I don't think performance really matters for deprecated functions (except specific situations), so it can be implemented in Erlang, and honestly less C code we have, more I feel happy.
src/libAtomVM/nifs.c
Outdated
static term nif_erlang_lists_subtract(Context *ctx, int argc, term argv[]); | ||
static term nif_zlib_compress_1(Context *ctx, int argc, term argv[]); | ||
|
||
|
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.
This empty new line polutes the diff, let's discard it.
d6c3c74
to
c3b2891
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.
Just a couple of minor changes required. I suggest squashing commits together as soon as you fix them so I can merge this PR very soon :)
src/libAtomVM/nifs.c
Outdated
.nif_ptr = nif_erlang_list_to_bitstring_1 | ||
}; | ||
static const struct Nif zlib_compress_nif = | ||
{ |
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'm not sure if this is a git rebase glitch or not, anyway let's make sure to rewrite this as:
static const struct Nif zlib_compress_nif = {
Before making any change I suggest squashing everything and rebasing, so clang-format workflow will pass CI.
CHANGELOG.md
Outdated
- Added `supervisor:which_children/1` and `supervisor:count_children/1` | ||
- Added `monitored_by` in `process_info/2` | ||
- Added mock implementation for `current_stacktrace` in `process_info` | ||
- Added `lists:keyfind`, `lists:keymember`, `lists:member` and `erlang:list_to_bitstring` |
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.
This is not accurate: lists:keyfind
, lists:keymember
, lists:member
were already available, so they don't belong to added.
We changed just how they are implemented. So in general, we leave out from the changelog implementation details that are not visible to the user, for those implementation details git log
is the right choice.
On the other hand erlang:list_to_bitstring
is new, so we can leave it here.
6ee6252
to
2f07971
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.
Let's make clang-format happy and merge this ;) I suggest to squash everything as soon as you fix it, so I can merge it very shortly.
src/libAtomVM/nifs.c
Outdated
return result; | ||
} | ||
|
||
|
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.
clang-format is not happy about this extra line:
@@ -5844,7 +5844,6 @@
return result;
}
-
static term nif_lists_member(Context *ctx, int argc, term argv[])
{
UNUSED(argc)
…nt erlang:list_to_bitstring Signed-off-by: Mateusz Front <mateusz.front@swmansion.com>
2f07971
to
bb901b8
Compare
Credit: @FKubisSWM, @TheSobkiewicz
These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).
SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later