Herb-AI / HerbSearch.jl

Search procedures and synthesizers for Herb.jl
https://herb-ai.github.io/
MIT License
7 stars 0 forks source link

Adding a macro to run iterators for a fixed amount of time #90

Open sebdumancic opened 4 months ago

sebdumancic commented 4 months ago

Adds two macros:

nicolaefilat commented 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)
nicolaefilat commented 4 months ago

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.

nicolaefilat commented 4 months ago

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.

  1. Save the file as main.c
  2. Compile using gcc main.c -pthread -o main
  3. Run code without killing the thread to show that it waits forever ./main
  4. Ctrl+C to stop the thing that waits forever.
  5. Run code with killing thread after 1 second ./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. 
ReubenJ commented 4 months ago

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...

nicolaefilat commented 4 months ago

@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

ReubenJ commented 4 months ago

It compiles just fine, but the thread is never actually killed. It also doesn't print any errors either.

nicolaefilat commented 4 months ago

I see, yes in that case is not a portable solution.

ReubenJ commented 3 months ago

Sorry, I accidentally deleted dev, triggering this PR to close.