代码之家  ›  专栏  ›  技术社区  ›  xji

如果julia脚本是从命令行运行的,那么每次都需要重新编译它吗?

  •  5
  • xji  · 技术社区  · 6 年前

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

    Profiling 文档的一节建议首先在repl中运行一次目标函数,以便在分析之前已经编译了它。但是,如果脚本相当复杂,并且被设置为在命令行中运行,并带有参数,该怎么办?当 julia 进程完成,我第二次运行脚本,是否再次执行编译?像这样的帖子 https://stackoverflow.com/a/42040763/1460448 , Julia compiles the script every time? 给出相互矛盾的答案。当茱莉亚不断进化的时候,它们看起来也很老了。

    在我看来,第二次跑步的时间和第一次跑步的时间差不多。启动时间比较长。我应该如何优化这样的程序?添加 __precompile__() 似乎根本没有改变执行时间。

    另外,当我想描述这样一个程序时,我应该怎么做?所有关于分析的资源都在repl中讨论了这一点。

    2 回复  |  直到 6 年前
        1
  •  3
  •   Colin T Bowers    6 年前

    如果我错了,请纠正我,但听起来你好像写了一个很长的剧本,比如说, myfile.jl ,然后从操作系统命令行调用 julia myfile.jl args... . 这是对的吗?而且,听起来像 myfile.jl文件 不是以函数的方式定义太多,而是一系列命令。这是对的吗?如果是这样,那么正如对这个问题的评论所建议的那样,这不是朱莉娅的典型工作流程,原因有二:

    1)从命令行呼叫茱莉亚 julia myfile.jl参数… 相当于打开一个repl,运行一个 include 命令打开 myfile.jl文件 ,然后关闭repl。最初的呼唤 包括 将编译中的操作所需的任何方法 myfile.jl文件 ,这需要时间。但既然你是从命令行运行,一旦 包括 完成后,repl会自动关闭,所有编译的代码都会被丢弃。这就是dnf的意思,当他说推荐的工作流是在一个repl会话中工作时,不要关闭它,直到您完成了一天的工作,或者除非您有意重新编译您正在使用的所有方法。

    2)即使您在一个repl会话中工作,它也是 极其 重要的是把你做的每件事都用函数包装起来(这是一个与类似于Matlab的语言截然不同的工作流)。如果您这样做,julia将为每个函数编译方法,这些方法专门用于您正在使用的输入参数的类型。这就是朱莉娅跑得快的根本原因。一旦编译了一次方法,它就可以在整个repl会话中使用,但是在关闭repl时会被释放。关键的是,如果不将操作包装在函数中,则不会进行这种专门的编译,因此您可能会期望非常慢的代码。在茱莉亚,我们称之为“在全球范围内工作”。注意,julia的这个特性鼓励一种编码风格,这种风格包括将任务分解成许多小的专门功能,而不是一个由1000行代码组成的庞然大物。这是个好主意,原因有很多。(在我自己的代码库中,许多函数都是一行代码,大多数是5行或更少)

    如果你在茱莉亚工作,以上两点对你的理解至关重要。但是,一旦您对它们感到满意,我建议您将所有功能都放在 modules ,然后在需要时从活动REPL会话调用模块。这还有一个额外的优势,您可以添加一个 __precompile__() 语句,然后julia将在该模块中预编译一些(但不一定全部)代码。完成此操作后,关闭repl时,模块中的预编译代码不会消失,因为它存储在硬盘上的.ji文件中。所以您可以启动一个新的repl会话,键入 using MyModule ,并且您的预编译代码立即可用。它只需要重新编译,如果你改变了模块的内容(这一切都是自动发生的)。

        2
  •  6
  •   Tasos Papastylianou    6 年前

    我有点不同意我的同事。有绝对有效的场景可以依赖于运行julia脚本。例如,当你有一个脚本管道(例如Matlab、Python等)时,你需要在其中插入一个Julia脚本,并从shell脚本控制整个管道。但是,不管是什么用例,说“只使用repl”并不是这个问题的正确答案,即使 不能 提出“有效”的方案,这仍然是一个值得直接回答的问题,而不是一个解决办法。

    我同意的观点是,拥有适当代码的解决方案是将所有需要预编译的关键代码打包到模块中,并且只将所有外部命令(除了最外部的命令)保留在脚本的顶层。这与Matlab或C++世界截然不同,在这里,你需要编写完整的函数,并且只将脚本/ main函数视为一种非常简短的顶层入口点,其工作是简单地准备初始环境,然后运行那些更专业化的F。相应的功能。

    以下是我的意思的一个例子:

    # 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脚本pipeli的一部分。一个进程,您也可以将参数传递给。这个 myscript.jl 如果有必要,脚本只需将这些传递给导入的模块函数,并执行您并不特别关心它们是否被编译/优化的任何其他命令,例如执行基准测试、提供脚本使用说明等。