Open sebdumancic opened 4 months ago
I tested the @timed_exec
macro but it does not work with code that is not sleeping and is only doing CPU bound tasks.
julia> @time @timed_exec 1 begin
arr = [] # do heavy work
for i ∈ 1:1e9
push!(arr, i)
pop!(arr) # pop and push to not overload the memory
end
println("Done executing task...")
end
Done executing task... 12.893823 seconds (1.00 G allocations: 14.903 GiB, 2.50% gc time, 0.29% compilation time)
However, if the task yeilds voluntary the control from the task or does any sort of IO then it works.
julia> @time @timed_exec 1 begin
arr = []
for i ∈ 1:1e9
push!(arr, i)
pop!(arr)
print(i)
end
println("Done executing task...")
end
// a lot of output and then
1.027285 seconds (400.43 k allocations: 36.622 MiB, 0.40% gc time, 4.95% compilation time)
# here I sleep on purpose
julia> @time @timed_exec 1 begin
arr = []
for i ∈ 1:1e9
push!(arr, i)
pop!(arr)
sleep(0.01)
end
println("Done executing task...")
end
1.027604 seconds (31.47 k allocations: 2.104 MiB, 3.88% compilation time)
From what I understand, in Julia a Task
is just like a coroutine that has to yield in order to be "swapped" out. If it is just doing intensive CPU computations, I don't think there is an easy way to stop it.
In C there is pthread_cancel
that can stop even though some thread is only doing CPU tasks.
Here is an example that shows what I mean.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void* calls(void* ptr)
{
// set the cancel type to asynchronous
int rs = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
if (rs != 0) {
perror("Thread pthread_setcanceltype failed\n");
exit(EXIT_FAILURE);
}
int i = 0;
while(1){
i++;
}
return i;
}
int main(int argc, char *argv[])
{
// if more than one argument is given than kill the thread
int killthread = (argc > 1) ? 1 : 0;
printf("Kill thread is set to %d\n", killthread);
// NULL when no attribute
pthread_t thread;
// calls is a function name
pthread_create(&thread, NULL, calls, NULL);
printf("Sleeping for 1 second\n");
sleep(1);
if (killthread) {
int rs = pthread_cancel(thread);
if (rs != 0) {
perror("Thread cancelation failed\n");
exit(EXIT_FAILURE);
}
printf("cancelled thread\n");
}
pthread_join(thread, NULL);
printf("Done!\n");
return 0;
}
To reproduce.
main.c
gcc main.c -pthread -o main
./main
./main 4
. This should wait about 1 second
On my machine I get
$ time ./main 4
Kill thread is set to 1
Sleeping for 1 second
cancelled thread
Done!
real 0m1.004s user 0m1.002s sys 0m0.002s
I believe that the _magic_ happens because if a thread is set to async cancelability that means that it can be killed immediately.
> A thread's cancelation type, determined by
[pthread_setcanceltype(3)](https://man7.org/linux/man-pages/man3/pthread_setcanceltype.3.html), may be either asynchronous or deferred
(the default for new threads). Asynchronous cancelability means
that the thread can be canceled at any time (usually immediately,
but the system does not guarantee this).
This from the pthread [docs](https://man7.org/linux/man-pages/man3/pthread_cancel.3.html)
From what I remember from my OS courses, the OS runs a hardware timer that interrupts any running process/thread at fixed time steps (e.g. 1ms) to run the scheduling algorithm. I think that is the place where the check happens that a thread is cancelled or not, and the long-running thread is killed there.
I am not sure if this code works on Windows too.
I haven't gotten the chance to test the macros, but @nicolaefilat, your C program doesn't seem to work on Mac/ARM, so it might be the case that this behavior is very OS-dependent. I'm not sure...
@ReubenJ I tested un Ubuntu. Where do you get a compile error?
From what I know, the pthread library should be on MAC too. https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/pthread.3.html
It compiles just fine, but the thread is never actually killed. It also doesn't print any errors either.
I see, yes in that case is not a portable solution.
Sorry, I accidentally deleted dev
, triggering this PR to close.
Adds two macros:
@timed_exec(seconds, expr::Expr)
for running an expression for a fixed amount seconds@timedfor(var_iter_expr::Expr, body_expr::Expr, seconds::Int64)
for a timed for loop