-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Add Unix.nonblock_single_write and Unix.nonblock_read #13024
base: trunk
Are you sure you want to change the base?
Conversation
31629bb
to
0faf927
Compare
I did more serious benchmarks (see code) and the gain is between 10 and 20%, larger with larger buffer, arround 5% with 1 byte buffer, but very noisy measure! My laptop is not very stable for benchmark. I would be happy if someone could test on a stable computer for benchmark Here is the test I use (nonblocking IO, without select): Compile with: Run with (second integer is buffer size, first is number of buffer written): |
I obtain similar speedups in the benchmark (between 25 and 30 %). I am reviewing this PR. |
My laptop was not so good, I am happy that you got 25-30%. I was considering write also the code for write/read on big array. Should this be in the same PR ? |
We probably should wait for the opinion from a maintainer about the current design before extending it. Personally, I think the addition is pleasantly self-contained. Sure, the API doesn’t prevent its user from calling the functions on blocking file descriptors, but the documentation makes this danger very explicit. Preventing such blocking calls by storing the blocking/non-blockingness in the file descriptor would require a much larger PR and I’m not sure the gain would be worth the cost. |
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 approach seems sane, I just noticed a few issues in the details of the code. We should also test that the functions run as expected on Windows.
Edit.: thanks for the contribution!
Co-authored-by: Olivier Nicole <[email protected]>
Co-authored-by: Olivier Nicole <[email protected]>
Co-authored-by: Olivier Nicole <[email protected]>
Co-authored-by: Olivier Nicole <[email protected]>
Co-authored-by: Olivier Nicole <[email protected]>
Co-authored-by: Olivier Nicole <[email protected]>
Co-authored-by: Olivier Nicole <[email protected]>
We really need to test this code on a windows platform. Just running the benchmark given above + may be a test for some errors. |
I agree, and one way to do it would be to add a small test of these functions in However I don’t want to encourage to implement without being sure that the PR has sufficient agreement. I would be more at peace if another maintainer validated the current design. |
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 technical comments. Going over the discussion in the motivating issue #11992, there was no opposition but also no clear opinion in favour by the other participants (@gasche and @xavierleroy). Technically, the PR seems sound, but the "safe" part of it is a bit unsatisfying (if we use this call with a blocking file descriptor, the whole system will block). @gasche, @xavierleroy: any opinions?
@@ -321,6 +321,12 @@ void caml_uerror(const char *cmdname, value cmdarg) | |||
caml_unix_error(errno, cmdname, cmdarg); | |||
} | |||
|
|||
CAMLprim void caml_unix_uerror(value msg, value cmdarg) { | |||
CAMLparam0(); | |||
caml_uerror(String_val(msg), cmdarg); |
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.
Looking at the source of caml_uerror
, it looks like this is safe (the GC will not be triggered before String_val(msg)
is copied), but it is subtle and fragile (a change in caml_uerror
can silently cause a segfault here). Can we find a more robust way of writing this code?
if (Descr_kind_val(fd) == KIND_SOCKET) { | ||
SOCKET s = Socket_val(fd); | ||
ret = recv(s, &Byte(buf, ofs), len, 0); | ||
if (ret == SOCKET_ERROR) ret = -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.
The error handling here is missing something: in case of error you need to call caml_win32_maperr(WSAGetLastError())
in order to set errno
correctly. See what is done in the usual read
call for inspiration.
// The write handle for an anonymous pipe has been closed. We match the | ||
// Unix behavior, and treat this as a zero-read instead of a Unix_error. | ||
ret = 0; | ||
} |
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.
Idem; here one should call caml_win32_maperr(GetLastError())
.
@@ -390,6 +390,12 @@ val read_bigarray : | |||
(** Same as {!read}, but read the data into a bigarray. | |||
@since 5.2 *) | |||
|
|||
val nonblock_read : file_descr -> bytes -> int -> int -> int | |||
(** Same as {!read}, but does not release the global lock nor copy the | |||
bytes read. It is only safe to use with non blocking file descriptor, |
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 is not completely clear what does "safe" means here. What about "It should only be used with ...".
if (Descr_kind_val(fd) == KIND_SOCKET) { | ||
SOCKET s = Socket_val(fd); | ||
ret = send(s, &Byte(buf, ofs), len, 0); | ||
if (ret == SOCKET_ERROR) ret = -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.
Error handling in this function is missing the same as in the "read" function.
Indeed and as such I find the name to be quite misleading. It may feel absurd but I think |
I really do not know a good name ;-) |
Co-authored-by: Nicolás Ojeda Bär <[email protected]>
Here are a few thoughts:
|
Co-authored-by: Nicolás Ojeda Bär <[email protected]>
Co-authored-by: Nicolás Ojeda Bär <[email protected]>
Co-authored-by: Nicolás Ojeda Bär <[email protected]>
I like |
This function are around 30% faster when avoiding the copying which is necessary if one release the global lock.
They are very useful for fast modern server doing non blocking IO. This addresses issue #11992. Note I could not test the windows code!