我已经读过Julia拥有Macros,但是我不确定Julia提供的Macros是否是我正在考虑的宏。
我有以下表达:
Global.data[ Dates.value(Dates.Year(dtCursor)) - 2000, Dates.value(Dates.Month(dtCursor)), Dates.value(Dates.Day(dtCursor)), Dates.value(Dates.Hour(dtCursor)) + 1, Dates.value(Dates.Minute(dtCursor)) + 1, 1 ]
我会重复很多次。我想知道是否可以使用dtCursor作为参数的宏(在其他情况下可能是其他变量)为我键入所有内容。因此,我正在寻找通常在宏汇编程序中找到的宏扩展功能。
我绝对不想将此作为函数包含在内,因为此代码已执行了成千上万次,因此,我不想增加函数调用的开销。
我试过了:
macro readData(_dtCursor, value) return :( Global.data[ Dates.value(Dates.Year(_dtCursor)) - 2000, Dates.value(Dates.Month(_dtCursor)), Dates.value(Dates.Day(_dtCursor)), Dates.value(Dates.Hour(_dtCursor)) + 1, Dates.value(Dates.Minute(_dtCursor)) + 1, value ] ) end
后来被调用:
println(@readData(dtCursor, 1))
dtCursor
DateTime变量在哪里。
但我得到:
ERROR: LoadError: UndefVarError: _dtCursor not defined
我已经阅读了https://docs.julialang.org/en/v1/manual/metaprogramming/index.html#man-macros-1,但是真的很乐于帮助您理解这种情况下的处理方法。
我绝对不想将此作为函数包含在内,因为此代码已执行了成千上万次,因此,我不想增加函数调用的开销。
你肯定是错的。
您可能在某些语言中是正确的,但在JuliaLang中却不正确。
(我确实认为这是一个非常有用的问题,因为可以突出显示其他人不要这样做)
该函数调用是内联的,即使没有,我们@inline
在使用宏之前也想使用其他工具()。
宏用于语法转换。
如果您不进行语法转换,请在使用宏之前再考虑一下。
这是史蒂文·约翰逊(Steven G. Johnson)在juliacon上的主题演讲中提到的一个好观点的链接:“功能对于杰夫·贝赞森(Jeff Bezanson)来说已经足够好。不要试图超越杰夫·比扎森(Jeff Bezason)”
以下回答您的原始问题
using Dates using BenchmarkTools macro readData(_dtCursor, value) return :( Global.data[ Dates.value(Dates.Year($(esc(_dtCursor)))) - 2000, Dates.value(Dates.Month($(esc(_dtCursor)))), Dates.value(Dates.Day($(esc(_dtCursor)))), Dates.value(Dates.Hour($(esc(_dtCursor)))) + 1, Dates.value(Dates.Minute($(esc(_dtCursor)))) + 1, $value ] ) end function readData(_dtCursor, value) Global.data[ Dates.value(Dates.Year(_dtCursor)) - 2000, Dates.value(Dates.Month(_dtCursor)), Dates.value(Dates.Day(_dtCursor)), Dates.value(Dates.Hour(_dtCursor)) + 1, Dates.value(Dates.Minute(_dtCursor)) + 1, value ] end
您说这将运行10,000次。因此,为了安全起见,我将以100_000次使用为基准。
const Global = (; data=[join((y, m, d, h, M, s)," ") for y in 2000:2010, m in 1:3, d in 1:20, h in 1:10, M in 1:30, s in 1:30]); size(Global.data) length(Global.data) const sample_dts = map(1:100_000) do _ y, m, d, h, M, s = rand.(axes(Global.data)) dt = DateTime(y+2000, m, d, h-1, M-1) end; func_demo() = [readData(dt, 3) for dt in sample_dts]; macro_demo() = [@readData(dt, 3) for dt in sample_dts]; @btime func_demo() @btime macro_demo()
julia> @btime macro_demo(); 5.409 ms (3 allocations: 781.34 KiB) julia> @btime func_demo(); 5.393 ms (3 allocations: 781.34 KiB)
julia> @code_typed macro_demo() CodeInfo( 1 ? %1 = Main.sample_dts::Core.Compiler.Const(DateTime[2002-01-18T04:19:00, 2001-01-19T08:22:00, 2006-02-08T04:07:00, 2011-01-08T09:03:00, 2006-02-10T06:18:00, 2002-03-12T00:05:00, 2011-02-20T08:29:00, 2011-02-20T07:12:00, 2005-01-13T03:22:00, 2006-01-01T00:29:00 … 2005-03-10T04:29:00, 2002-03-12T09:11:00, 2002-03-11T00:28:00, 2007-02-12T02:26:00, 2003-02-15T07:29:00, 2009-01-01T02:02:00, 2009- 01-03T02:11:00, 2001-02-16T03:16:00, 2004-01-17T05:12:00, 2010-02-02T05:10:00], false) ? %2 = %new(Base.Generator{Array{DateTime,1},getfield(Main, Symbol("##50#51"))}, getfield(Main, Symbol("##50#51"))(), %1)::Base.Gen erator{Array{DateTime,1},getfield(Main, Symbol("##50#51"))} ? %3 = invoke Base.collect(%2::Base.Generator{Array{DateTime,1},getfield(Main, Symbol("##50#51"))})::Array{String,1} ??? return %3 ) => Array{String,1} julia> @code_typed getfield(Main, Symbol("##50#51")).instance(1) # check the internals ? %1 = %1 = Main.Global::Core.Compiler.Const((#==GIANT Inlined COnst==#) ? %2 = Base.getfield(%1, :data)::Array{String,6} ? %3 = Base.sub_int(dt, 2000)::Int64 ? %4 = Base.add_int(dt, 1)::Int64 ? %5 = Base.add_int(dt, 1)::Int64 ? %6 = Base.arrayref(true, %2, %3, dt, dt, %4, %5, 3)::String ??? return %6 ) => String julia> @code_typed func_demo() CodeInfo( 1 ? %1 = Main.sample_dts::Core.Compiler.Const(DateTime[2002-01-18T04:19:00, 2001-01-19T08:22:00, 2006-02-08T04:07:00, 2011-01-08T09:03:00, 2006-02-10T06:18:00, 2002-03-12T00:05:00, 2011-02-20T08:29:00, 2011-02-20T07:12:00, 2005-01-13T03:22:00, 2006-01-01T00:29:00 … 2005-03-10T04:29:00, 2002-03-12T09:11:00, 2002-03-11T00:28:00, 2007-02-12T02:26:00, 2003-02-15T07:29:00, 2009-01-01T02:02:00, 2009- 01-03T02:11:00, 2001-02-16T03:16:00, 2004-01-17T05:12:00, 2010-02-02T05:10:00], false) ? %2 = %new(Base.Generator{Array{DateTime,1},getfield(Main, Symbol("##43#44"))}, getfield(Main, Symbol("##43#44"))(), %1)::Base.Gen erator{Array{DateTime,1},getfield(Main, Symbol("##43#44"))} ? %3 = invoke Base.collect(%2::Base.Generator{Array{DateTime,1},getfield(Main, Symbol("##43#44"))})::Array{String,1} ??? return %3 ) => Array{String,1} julia> @code_typed getfield(Main, Symbol("##43#44")).instance(1) CodeInfo( 1 ? %1 = Main.Global::NamedTuple{(:data,),Tuple{Array{String,6}}} ? %2 = Base.getfield(%1, :data)::Array{String,6} ? %3 = Base.sub_int(dt, 2000)::Int64 ? %4 = Base.add_int(dt, 1)::Int64 ? %5 = Base.add_int(dt, 1)::Int64 ? %6 = Base.arrayref(true, %2, %3, dt, dt, %4, %5, 3)::String ??? return %6 ) => String
两者之间的生成器功能之间存在很小的差异。当内联时,值变为a Compliler.Const
或a NamedTuple
,但是之后LLVM的差异也是如此(我认为(检查@code_llvm
您是否真的很感兴趣。但是我们已经对杂草非常了解了。)
长期指导您进行任何优化的基准测试。还应该分析代码以确定哪些值得优化。该函数仅被调用10,000次,并且不分配巨型数组等,可能不值得过多担心。特别是如果您只担心函数调用开销,这仅是少数几个CPU周期。