Julia ---- 反射機制 Symbol 通過給定的字符串調用函數

1、正確的方式

建議的方法是將函數名轉換爲符號,然後在適當的命名空間中查找該符號:

fn = "time"

Symbol(fn)
#:time

getfield(Main, Symbol(fn))
#time (generic function with 2 methods)

getfield(Main, Symbol(fn))()
#1.580791892769e9 ,相當於調用 time() 函數

您可以將這裏的Main更改爲任何模塊,以便只查看該模塊中的函數。這允許您將可用的函數集限制爲僅適用於該模塊中可用的函數集。您可以使用“裸模塊”創建一個只包含你用到函數的名稱空間,而無需從Base導入所有的默認命名空間。

 

2、錯誤的方式

雖然不建議使用,但是許多人似乎還是會使用的另一種方法是爲調用函數的代碼構造一個字符串,然後解析該字符串並對其求值。例如:

julia> eval(parse("$fn()")) # NOT RECOMMENDED 1.464877410113412e9

在 Julia 1.0版本後,這種不安全的使用方式已經被禁止了,會報異常。

3、性能分析

如果要在內部循環中動態調用指定的函數,或者將其作爲遞歸計算的一部分,那麼每次調用該函數時都要避免執行查找getfield。在這種情況下,您只需先對其進行const綁定,然後才能再迭代/遞歸過程中使用該指定函數。例如:

#將角度(度)轉換爲弧度函數
fn = "deg2rad" # converts angles in degrees to radians

#先用const 定義
const f = getfield(Main, Symbol(fn))

#然後遞歸中使用
function fast(n)
    t = 0.0
    for i = 1:n
        t += f(i)
    end
    return t
end

@time fast(10^6) # 第一次使用會執行JIT編譯

@time fast(10^6) # 第二次會更快

爲了獲得最佳性能,必須使用常量綁定f,否則編譯器無法知道你是不是會在其他時候更改f指向其他函數(甚至不是函數的某個函數),因爲編譯器必須在循環 中 的每次循環迭代時動態查找f的代碼。在這裏,由於f是const,編譯器知道f不能更改,所以它可以快速找到代碼,直接調用正確的函數。編譯器有時甚至可以做得更好——在這種情況下,它實際上是內部關聯了deg2rad函數的實現

#可以看到代碼的執行情況
@code_llvm fast(100000)


#下面的內容,跟Julia的版本有關,不同的版本 打印出的內容有差異
;  @ D:\Julia\StringToFunction:34 within `fast'
; Function Attrs: uwtable
define double @julia_fast_13725(i64) #0 {
top:
;  @ D:\Julia\StringToFunction:35 within `fast'
; ┌ @ range.jl:5 within `Colon'
; │┌ @ range.jl:275 within `Type'
; ││┌ @ range.jl:280 within `unitrange_last'
; │││┌ @ operators.jl:341 within `>='
; ││││┌ @ int.jl:424 within `<='
       %1 = icmp sgt i64 %0, 0
; └└└└└
  br i1 %1, label %L7.L12_crit_edge, label %L29

L7.L12_crit_edge:                                 ; preds = %top
  br label %L12

L12:                                              ; preds = %L12, %L7.L12_crit_edge
  %value_phi3 = phi double [ 0.000000e+00, %L7.L12_crit_edge ], [ %4, %L12 ]
  %value_phi4 = phi i64 [ 1, %L7.L12_crit_edge ], [ %6, %L12 ]
;  @ D:\Julia\StringToFunction:36 within `fast'
; │┌ @ int.jl:53 within `+'
    %6 = add nuw i64 %value_phi4, 1
; └└
  br i1 %5, label %L29, label %L12

L29:                                              ; preds = %L12, %top
  %value_phi9 = phi double [ 0.000000e+00, %top ], [ %4, %L12 ]
;  @ D:\Julia\StringToFunction:38 within `fast'
  ret double %value_phi9
}

如果需要在多個地方動態指定函數,並且使用的是Julia 0.5以上版本,則可以將要調用的函數作爲參數傳遞:

function fast(f,n)
    t = 0.0
    for i = 1:n
        t += f(i)
    end
    return t
end

@time fast(getfield(Main, Symbol(fn)), 10^6)
# 0.001661 seconds (6 allocations: 192 bytes)
@time fast(getfield(Main, Symbol(fn)), 10^6)
#0.001246 seconds (6 allocations: 192 bytes)

這將生成與上面的單參數fast相同的fast代碼,但將爲調用它的每個不同函數f生成一個新版本。

 

發佈了94 篇原創文章 · 獲贊 74 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章