Chris Martin
Chris Martin

Reputation: 45

Set a time limitation on algorithm in Julia

I have a Julia code which runs for quite sometime. I want to run the code for 3 hours and then terminate it. Does anyone know what is the most efficient way to do this. Would appreciate any advice. Thanks

Upvotes: 1

Views: 1350

Answers (3)

godfryd
godfryd

Reputation: 582

Here's a solution using threads. I'm just begginging coding in Julia so it's not high quality code, but it works. You first need to wrap your function as a Task, by using the @task macro, and then call runTask():

    function runTask(origtask:: Task, timeoutms:: Int)
        startTime = Dates.datetime2epochms(now())

        function internal_task(taskFinished)
            try
                schedule(origtask)
                yield()
                wait(origtask)
                if istaskdone(origtask)
                    taskFinished[] = true
                    Base.task_result(origtask)
                else
                    throw(ErrorException("Task is not done even after wait() for it to finish - something is wrong"))
                end
            catch e
                @warn "Error while processing task: $e"
                taskFinished[] = true
                missing
            end
        end

        taskFinished = Threads.Atomic{Bool}(false)
        thread = Threads.@spawn internal_task(taskFinished)
        while !taskFinished[] && (Dates.datetime2epochms(now()) - startTime) < timeoutms
            sleep(0.1)
        end
        if taskFinished[]
            return fetch(thread)
        end
        # Task timeouted
        origtask.exception = InterruptException()
        return missing
    end # function

The worst part of this code is active waiting using sleep(0.1). I guess I could get rid of it by spawning one more thread, so there would be 3 in total: one would execute the actual work from the origtask and after doing it would notify a condition, one would wait on the mentioned condition and another one would sleep for the timeout number of seconds and then notify the condition.

Upvotes: 2

Przemyslaw Szufel
Przemyslaw Szufel

Reputation: 42214

I recommend using Distributed to spawn your function as a new process and control it's time (I believe I have been answering similar question but I cannot find the answer).

Here is the code:

using Distributed
function run_with_timeout(timeout::Int,f::Function, wid::Int)
    result = RemoteChannel(()->Channel{Tuple}(1));
    @spawnat wid put!(result, (f(),myid()))
    res = (:timeout, wid)
    time_elapsed = 0.0
    while time_elapsed < timeout && !isready(result)
        sleep(0.5)
        time_elapsed += 0.5
    end
    if !isready(result)
        println("Timeout! at $wid")
    else
        res = take!(result)
    end
    return res
end

You can use it like this (please note how external packages are imported):

wid = addprocs(1)[1]
@everywhere using DataFrames
@everywhere function ff()
    sleep(2)
    #code fir making heavy computations
    #this is the place to write whatever you need
    return DataFrame(x=[1,2],y=[1,3])
end

Now let us run it. Note that the second returned value is the workerid to run computations (you might want to remove it):

julia> run_with_timeout(10,() ->(try;ff();catch ee;dump(ee);end ),wid)
(2×2 DataFrame
│ Row │ x     │ y     │
│     │ Int64 │ Int64 │
├─────┼───────┼───────┤
│ 1   │ 1     │ 1     │
│ 2   │ 2     │ 3     │, 2)

If we give our code only 1 sec to execute it will fail:

julia> run_with_timeout(1,() ->(try;ff();catch ee;dump(ee);end ),wid)
Timeout! at 2
(:timeout, 2)

Finally, since the timeout occured the process should be killed (otherwise it would continue to execute):

rmprocs(wid)

Upvotes: 1

Bill
Bill

Reputation: 6086

@async and @sync are really useful for coroutine type process control in Julia. You can start an async process that calls exit at a later point so as to terminate the entire program:

function killafterseconds(s)
    @async begin 
        sleep(s)
        println("terminating after $s seconds")
        exit(0)
    end
end

function countdown(n)
    for t in n:-1:0
        println(t)
        sleep(1)
    end
end

killafterseconds(10)

countdown(10000)

Upvotes: 4

Related Questions