使用宏避免输入Julia

M.E*_*.E. 5 julia

我已经读过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
]
Run Code Online (Sandbox Code Playgroud)

我会重复很多次。我想知道是否可以使用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
Run Code Online (Sandbox Code Playgroud)

后来被调用:

println(@readData(dtCursor, 1))
Run Code Online (Sandbox Code Playgroud)

dtCursorDateTime变量在哪里。

但我得到:

ERROR: LoadError: UndefVarError: _dtCursor not defined
Run Code Online (Sandbox Code Playgroud)

我已经阅读了https://docs.julialang.org/en/v1/manual/metaprogramming/index.html#man-macros-1,但是真的很乐于帮助您理解这种情况下的处理方法。

Lyn*_*ite 8

使用功能

我绝对不想将此作为函数包含在内,因为此代码已执行了成千上万次,因此,我不想增加函数调用的开销。

肯定是错的。
您可能在某些语言中是正确的,但在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
Run Code Online (Sandbox Code Playgroud)

进行基准测试。

您说这将运行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()

Run Code Online (Sandbox Code Playgroud)

他们基准相同

julia> @btime macro_demo();
  5.409 ms (3 allocations: 781.34 KiB)

julia> @btime func_demo();
  5.393 ms (3 allocations: 781.34 KiB)
Run Code Online (Sandbox Code Playgroud)

实际上,它们专门用于(基本上)相同的代码。

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
Run Code Online (Sandbox Code Playgroud)

两者之间的生成器功能之间存在很小的差异。当内联时,值变为a Compliler.Const或a NamedTuple,但是之后LLVM的差异也是如此(我认为(检查@code_llvm您是否真的很感兴趣。但是我们已经对杂草非常了解了。)

首先,这可能是要优化的错误代码。

长期指导您进行任何优化的基准测试。还应该分析代码以确定哪些值得优化。该函数仅被调用10,000次,并且不分配巨型数组等,可能不值得过多担心。特别是如果您只担心函数调用开销,这仅是少数几个CPU周期。