【问题标题】:Julia: inject code containing keyword into sciptJulia:将包含关键字的代码注入脚本
【发布时间】:2021-01-07 10:24:52
【问题描述】:

我有一个包含 for 循环的函数。我想为该函数添加一个参数,让我选择使用并行运行循环 Threads.@threads for i in ...。 因此我只需要在循环前面注入Threads.@threads。宏不起作用,因为它们无法处理关键字。 或者我可以有类似的东西

if parrallel
    inject("Threads.@threads for i in 1:n")
else
    inject("for i in 1:n")
end

    loop content....
end

我找不到任何方法来插入这样的代码。怎么办?

当然可以选择将整个循环放在一个函数中,然后在函数上使用包含 for 循环的 if else,但我更喜欢其余代码。

【问题讨论】:

  • 及时注入代码听起来不是一个好的或简单的方法。这里有一个类似的问题:stackoverflow.com/questions/32871620/….
  • 我读到了,但我认为提出的问题不适用,因为我想在一个函数内而不是从函数外部执行它。
  • 你想注入这样的代码的原因是什么?在我看来,这似乎是一种非常奇怪的编程方式。它不能正确编译,很难做到,也没有任何明显的优势。我错了吗?
  • 可能是无能 ;) 我确实认为它可能不是最佳的,但基本上我只是在寻找一种方法来尽可能多地留下代码。

标签: macros julia keyword


【解决方案1】:

当然可以选择将整个循环放在一个函数中,然后在函数上使用包含 for 循环的 if else,但我更喜欢其余代码。

请注意,像do blocks 这样的高阶函数和语法糖使此类解决方案的开发相对简单且易于阅读:

您可以开始定义两个抽象出 for 循环的高阶函数。

# This one is basically `Base.foreach`
function sequential_for(f, iterable)
    for i in iterable
        f(i)
    end
end

# A thread-parallel version
function parallel_for(f, iterable)
    Threads.@threads for i in iterable
        f(i)
    end
end

然后你的函数可以动态决定它想要使用哪个版本的 for 循环:

function my_fun(n; parallel=false)
    for_loop = parallel ? parallel_for : sequential_for
    
    x = zeros(Int, n)

    # The do syntax avoids having to either
    # - define the loop body as a named function elsewhere, or
    # - put an hard-to-read lambda directly as argument to `for_loop`
    for_loop(1:n) do i
        x[i] = Threads.threadid()
        sleep(0.1)  # Let's make sure we see the effect of parallelism :-)
    end
    return x
end

使用示例:

julia> @time my_fun(10)
  1.025307 seconds (299 allocations: 17.109 KiB)
10-element Array{Int64,1}:
 1
 1
 1
 1
 1
 1
 1
 1
 1
 1

julia> @time my_fun(10, parallel=true)
  0.235430 seconds (18.44 k allocations: 979.714 KiB)
10-element Array{Int64,1}:
 1
 1
 2
 2
 3
 4
 5
 6
 7
 8

【讨论】:

    【解决方案2】:

    及时插入代码不能很好或很容易地工作。编译器不知道该做什么,它根本不会被优化,也不安全。对于类似的问题,请参阅:Julia: inject code into function

    我会通过为 for 循环编写一个单独的函数(我认为更简洁的方式)来编写代码,或者将 for 循环留在你的第一个函数中,然后把它写成双倍。

    类似:

    function forloopcontent()
        println(Threads.threadid())
    end
    
    function f(parallel::Bool)
        if parallel
            Threads.@threads for i in 1:10
                forloopcontent()
            end
        else for i in 1:10
                forloopcontent()
            end
        end
    end
    

    否则,您还可以为并行版本编写另一个函数,为普通版本编写两种不同的方法。即:

    function f(;parallel::Bool=false)
        parallel ? (return par_f()) : (return f())
    end 
    
    function f()
        for i in 1:10
            println(Threads.threadid())
        end
    end
    
    function par_f()
        Threads.@threads for i in 1:10
            println(Threads.threadid())
        end
    end
    

    并行版本可以称为f(;parallel=true),非并行版本可以称为f(;parallel=false)

    【讨论】:

      猜你喜欢
      • 2012-11-14
      • 2015-12-28
      • 1970-01-01
      • 2019-08-13
      • 1970-01-01
      • 2016-10-09
      • 2020-08-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多