如果一个 Julia 脚本是 运行 来自命令行,是否需要每次都重新编译?

If a Julia script is run from the command line, does it need to be re-compiled every time?

我已经通读了很多文档和问题,但我仍然对此感到困惑。

Profiling section of the documentation it's suggested to first run the target function in the REPL once, so that it's already compiled before being profiled. However, what if the script is fairly complicated and is inteded to be run in the command line, taking arguments? When the julia process finishes and I run the script the second time, is the compilation performed again? Posts like , Julia compiles the script every time?给出相互矛盾的答案。他们也似乎老了,而 Julia 也在不断进化。

在我看来,第二个 运行 花费的时间与我的经验中的第一个 运行 完全一样。启动时间相当长。我应该如何优化这样的程序?添加 __precompile__() 似乎根本没有改变执行时间。

还有,我要profile这样的程序怎么办?所有关于分析的资源都在 REPL 中讨论这样做。

如果我错了请纠正我,但听起来你写了一些很长的脚本,比如 myfile.jl,然后从你的 OS 命令行调用 julia myfile.jl args....这个对吗?此外,听起来 myfile.jl 并没有以函数的方式定义太多,而只是一系列命令。这个对吗?如果是这样,那么正如对该问题的评论中所建议的那样,这不是 julia 的典型工作流程,原因有二:

1)从命令行调用julia,即julia myfile.jl args...相当于打开一个REPL,运行一个include命令在myfile.jl,然后关闭回复。对 include 的初始调用将编译 myfile.jl 中的操作所需的任何方法,这需要时间。但是由于您是从命令行 运行,一旦 include 完成,REPL 会自动关闭,所有编译的代码都会被丢弃。这就是 DNF 的意思,他说推荐的工作流程是在单个 REPL 会话中工作,并且在当天完成之前不要关闭它,或者除非你故意要重新编译你正在使用的所有方法。

2) 即使您在单个 REPL 会话中工作,非常 将您所做的几乎所有事情都包装在函数中是非常重要的(这与语言的工作流程非常不同像 Matlab)。如果这样做,Julia 将为每个函数编译专门针对您正在使用的输入参数类型的方法。这基本上就是 Julia 速度快的原因。一旦一个方法被编译一次,它在整个 REPL 会话中仍然可用,但是当你关闭 REPL 时它就会被丢弃。至关重要的是,如果您不将您的操作包装在函数中,那么这种专门的编译就不会发生,因此您可以期待非常慢的代码。在 julia 中,我们称之为 "working in the global scope"。请注意,Julia 的这一特性鼓励一种编码风格,包括将您的任务分解为许多小的专用函数,而不是一个由 1000 行代码组成的庞然大物。这是一个好主意,原因有很多。 (在我自己的代码库中,很多函数都是单行的,大多数是5行或更少)

如果你在 Julia 工作,以上两点对于理解是绝对重要的。但是,一旦您对它们感到满意,我建议您实际上将所有函数放在 modules 中,然后在需要时从活动的 REPL 会话中调用您的模块。这还有一个额外的好处,就是您只需在模块顶部添加一个 __precompile__() 语句,然后 julia 就会预编译该模块中的部分(但不一定是全部)代码。执行此操作后,模块中的预编译代码不会在您关闭 REPL 时消失,因为它以 .ji 文件的形式存储在硬盘上。因此,您可以启动一个新的 REPL 会话,键入 using MyModule,您的预编译代码将立即可用。如果你改变了模块的内容,它只需要重新编译(这一切都会自动发生)。

我有点不同意我同事的看法。在绝对有效的情况下,人们会依赖 运行ning julia 脚本。例如。当你有一个脚本管道(例如 matlab、python 等)并且你需要在所有脚本中间的某个地方插入一个 julia 脚本,并从 shell 脚本控制整个管道。但是,无论用例是什么,说 "just use the REPL" 都不是这个问题的正确答案,即使 无法 想出 "valid" 场景,这仍然是一个值得直接回答而不是解决方法的问题。

我同意的是,拥有适当代码的解决方案是将需要预编译的所有关键内容包装到模块中,并且只将除最外部命令之外的所有命令留在脚本顶层。无论如何,这与 matlab 或 C++ 世界并没有太大的不同,在那里你需要编写完整的函数,并且只将你的脚本/主函数视为某种非常简短的顶级入口点,其工作是简单地准备初始环境,然后 运行 相应的更专业的功能。

这是我的意思的一个例子:

# in file 'myscript.jl'
push!( LOAD_PATH, "./" )
import MyPrecompiledModule
println( "Hello from the script. The arguments passed into it were $ARGS" )
MyPrecompiledModule.exportedfun()

# in file 'MyPrecompiledModule.jl' (e.g. in the same directory as myscript.jl)
__precompile__()
module MyPrecompiledModule
  export exportedfun;
  function innerfun()
    println("Hello from MyPrecompiledModule.innerfun");
  end

  function exportedfun()
    innerfun()
    print("Hello from MyPrecompiledModule.exportedfun");
  end
end

在上述场景中,MyPrecompiledModule 的编译版本将在脚本中使用(如果不存在,将在您第一次 运行 脚本时编译) ,因此编译的任何优化都不会在脚本末尾丢失,但您仍然会得到一个独立的 julia 脚本,您可以将其用作 bash shell 脚本管道过程的一部分,您也可以传递参数给。 myscript.jl 脚本只需要在必要时将这些传递给导入的模块函数,并执行您不特别关心它们是否被编译/优化的任何其他命令,例如执行基准测试,提供脚本使用说明等