【问题标题】:How to measure the @async runtime of functions in Julia?如何测量 Julia 中函数的 @async 运行时?
【发布时间】:2020-10-20 22:32:57
【问题描述】:

我的代码如下,

function getImages()
    @async for i in 1:30
        load("path/to/image$i.jpg");
    end
end
   
@time @sync getImages()

这是在 Julia 中测量异步运行时的正确方法吗?我不想相信它,因为在同步运行中它需要 0.1 秒才能完成,而使用 @async 代码它只显示 0.000017 秒。

【问题讨论】:

    标签: asynchronous julia


    【解决方案1】:

    你需要稍微重新组织你的代码

    julia> function getImages()
               @sync for i in 1:30
                   @async begin
                       sleep(rand())
                       println("Image ", i)
                   end
               end
           end
    getImages (generic function with 1 method)
    
    julia> @time getImages()
    Image 2
    Image 5
    Image 15
    Image 30
    Image 4
    Image 7
    Image 20
    Image 3
    Image 22
    Image 18
    Image 11
    Image 29
    Image 9
    Image 24
    Image 16
    Image 6
    Image 8
    Image 14
    Image 19
    Image 21
    Image 13
    Image 1
    Image 17
    Image 10
    Image 27
    Image 25
    Image 23
    Image 28
    Image 26
    Image 12
      0.894566 seconds (650 allocations: 37.406 KiB)
    

    【讨论】:

    • 这看起来很异步,但是如果我再次使用 load("image.jpg") 而不是 sleep(rand()) 它会按顺序打印。对于读取文件,这是我们应该期待的吗?
    • Przemyslaw Szufel 在上面的答案中回答了它。 @async 本身并不并行运行,它同时运行它们。这意味着,该函数可以将控制权交给另一个函数,这样您就可以在同一线程上运行多个进程,以 100% 使用 CPU。例如,如果您从网络下载数据是有意义的:当进程等待服务器响应时,它可以将控制权交给另一个可以发送另一个请求的进程。但似乎load 操作并非如此,它 100% 的时间都在使用整个线程。
    【解决方案2】:

    这是正确的代码:

    function getImages()
        myimages = Vector{Any}(undef, 30) 
        # instead of Any use the correct type of whatever your load returns
        @sync for i in 1:30
            @async myimages[i] = load("path/to/image$i.jpg");
        end
        myimages 
    end   
    

    当您的 IO 很慢时,这种方法很有用。 但是请注意,此代码将仅使用单个线程。因此,如果 IO 不是您的性能瓶颈,那么这种并行化将无济于事。在这种情况下,您应该考虑使用线程。

    在开始 Julia 运行之前:

    set JULIA_NUM_THREADS=4
    

    或在 Linux 上

    export JULIA_NUM_THREADS=4
    

    并将您的功能更改为:

    function getImages()
        myimages = Vector{Any}(undef, 30) 
        # instead of Any use the correct type of whatever your load returns
        Threads.@threads for i in 1:30
            myimages[i] = load("path/to/image$i.jpg");
        end
        myimages 
    end
    

    【讨论】:

    • 非常感谢!我按照你在@threads 代码上所说的做了,但我遇到了一个我无法正常得到的错误。 Fatal error: ERROR: LoadError: TaskFailedException: concurrency violation detected
    • 您的 load 函数可能不是线程安全的(例如,依赖内部状态或函数调用之间共享的缓冲区)。在这种情况下,您将无法对其进行多线程处理(除非您将其修改为线程安全的)。在这种情况下,您仍然可以查看Distributed
    • 非常感谢您的帮助。我想我会坚持使用@distributed 一段时间。
    【解决方案3】:

    你的直觉就在这里!简短的回答是您没有正确使用@sync

    引用@sync的文档:

    等到所有词法封闭使用@async、@spawn、@spawnat 和@distributed 完成。封闭的异步操作抛出的所有异常都被收集并作为 CompositeException 抛出。

    这里的@async 不是@sync 表达式中的词法封闭。实际上,@sync 宏没有魔法。 @async@spawn@spawnat@distributed 表达式将创建任务。而@sync 只需等待他们完成。

    所以你手动这样做:

    julia> f() = @async sleep(1)
    
    julia> @time wait(f())
      1.002095 seconds (9 allocations: 848 bytes)
    

    【讨论】:

    • 感谢您的回答!!但是你能在我的代码中显示它吗?我尝试了所有组合,但它仍然按顺序打印数字。
    • 好吧。这是另一个问题。我猜你打算使用Base.Threads
    猜你喜欢
    • 2012-04-23
    • 2021-12-28
    • 2021-09-23
    • 1970-01-01
    • 2014-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多