【问题标题】:Am I using Julia right?我使用 Julia 对吗?
【发布时间】:2015-10-31 00:18:42
【问题描述】:

几个月前我开始使用 Julia,在听到人们对它的各种功能赞不绝口后决定试一试。我对它了解得越多,我就越喜欢它的风格,它将高级语言中易于表达的概念与注重速度和可用性相结合。我在 Julia 中实现了一个我也用 C++ 和 R 编写的模型,发现 Julia 版本的运行速度比 R 版本快得多,但仍然比 C++ 稍慢。即便如此,Julia 中的代码也比其他两种语言都更清晰。这很有价值,特别是当我对模型进行概括时,为扩大 Julia 代码的范围所做的工作量远远少于其他语言可能面临的可比工作量。

最近,我一直专注于让我的 Julia 代码运行得更快,因为我需要运行这个模型数万亿次。在这样做的过程中,我受到@code_warntype@time@profileProfileView 以及track-allocation 标志的指导。伟大的。这个工具包不如其他一些语言的分析工具好,但它仍然指出了很多瓶颈。

我发现我的代码中恰好具有我喜欢 Julia 的高级表达能力,当我重写这种表达能力以避免不必要的分配时,我失去了这种表达能力。作为一个简单的例子,我最近更改了一行代码,如下所示

sum([x*y for x in some_list, y in similar_list])

到一个遍历列表并添加到状态变量的循环。不是火箭科学,我理解为什么不必分配数组会快一点。实际上,它比 很多 更快。所以我做了类似的事情,避免使用字典或“感觉”适合问题的复合类型,当我可以手动跟踪临时并行数组中的索引时,我讨厌这种编码风格,但显然运行当我多次重复创建和简要使用小型数据结构的特定操作时,速度要快得多。

总的来说,这在很大程度上很好,因为我已经将编写短方法的说明牢记在心,因此构成我自己的短方法行为的高级方法不需要“担心”短方法如何方法有效;较短的代码读起来会很笨重,但不会让我的程序的核心读起来很笨拙。

但这让我想知道我是否“遗漏了什么”。如果该语言的全部意义(对于我作为一个不关心理论的最终用户而言)部分是将速度与易于开发/思考/阅读/维护等结合起来,即成为一种可写和可用的技术计算语言,那么这是否意味着我不应该花时间思考将一堆数字相加的最快方法,不应该重写“易于阅读”或优雅的代码,利用映射、过滤器和高级函数概念转化为“笨拙”的代码,重新发明轮子来表达这些东西,以跟踪无处不在的低级数组索引? (我的某些部分期望一种在其设计背后具有如此多智能的语言足够聪明,以“弄清楚”当我编写 sum([x*y]) 时它实际上不需要分配一个新数组,并且我只是太迟钝了,无法找出正确的词来告诉语言,除了手动“告诉它”整个循环业务。有一次我什至考虑写 @macros 将一些快速表达的代码“转换”成长但更快的循环表达式,但我认为如果我本质上试图扩展编译器的功能只是为了解决相当简单的问题,那么我一定是在考虑错误的问题,这就是导致我写这个问题的原因。)

也许答案是“如果你想要真正高性能的代码,无论如何你都要付出代价”。或者换一种说法,读取循环、数组、跟踪索引等令人讨厌的快速代码处于速度-易读性权衡空间的有效前沿。如果是这样,那是完全正确的,因此我不会说我对 Julia 的看法有所减少。我只是想知道这种编程风格是否真的处于前沿,或者我的经验是否是因为我只是没有用这种语言“很好”地编程。 (以此类推,请参阅问题 What is your most productive shortcut with Vim? ,其中公认的优秀答案本质上是 OP 只是没有“得到”它。)我怀疑即使我已经成功地让语言做很多事情我想要什么,我只是没有“得到”一些东西,但我不知道该要求什么,因为我担心我没有得到的东西对我来说是一个未知的未知数。

TL;DR: 是否期望 Julia 的最佳实践是我花费大量时间将我的高级函数调用“分解”为循环和数组方面的原始实现,以便更快性能,还是这表明我没有考虑正确使用/使用该语言进行编程?

【问题讨论】:

  • 嗨@Philip,您还在使用 Julia,您能否报告一下问题中的情况现在是否以及如何发生了变化?我相信已经努力使向量化代码更快,并且现在有更多基于宏和基于类型的功能进行优化,所以我想知道现在将高级代码“分解”为原始实现的需求是否减少了,如果所以多少。
  • 我仍然使用它,尽管我没有能力回答您的问题,因为我自己的需求已经从代码性能转向代码清晰度。所以我现在不知道 Julia 让编写“自动”执行良好的清晰代码变得多么容易——我只知道我一直在使用它,因为编写清晰代码很容易! :)

标签: performance coding-style julia


【解决方案1】:

我认为这个话题与 Julia 用户组 Does Julia really solve the two-language problem? 已经进行的讨论非常吻合,我想在这里引用该讨论中的一段:

@Stefan Karpinski:

每个人都有不同的风格和编写代码的水平 语言。有高度抽象的 C++,也有低级的 基本上是 C 的指针追逐 C++。即使在 C 中也有 void* 风格的编程,有效地动态输入 没有安全感。鉴于这个事实,我不确定是什么解决了这两个问题 从这篇文章的角度来看,语言问题看起来像 摆姿势。只执行一种编程风格听起来是个问题 对我来说,不是解决方案。相反,我认为朱莉娅的一个 最大的优势是它能够容纳非常广泛的 编程风格和级别。

我自己在 Julia 编程方面的经验表明,它可以填补现代编程语言的空白,可以为科学家、工程师和所有计算大师带来并行处理、套接字服务器等高级功能希望使用一体化编程语言以高效、可维护和可读的方式完成工作的务实程序员。
在我看来,您以正确的方式使用 Julia,与其他语言一样,Julia 代表了针对不同情况的不同编程风格,您可以优化速度瓶颈,并使其他部分更具可读性。您也可以使用Devectorize.jl 之类的工具来避免重写问题。

【讨论】:

  • 好的,我已经阅读了几次该讨论,我认为您的总结非常明智。简短的回答似乎是“你用同一种语言编写高级的东西和低级的东西是 Julia 的巨大优势之一。”我觉得这是一个很好的答案。
  • 顺便说一句,是的,Devectorize.jl 所做的正是我在考虑为特定案例编写宏时所想的。它的性能基准让我认为,出于许多目的,仅针对特定情况优化例程是值得的,但我会密切关注它的发展。
【解决方案2】:

这是一个很难令人满意地回答的问题。因此,我将尝试做一个简短的贡献,希望一个“锅”不同的意见可能就足够了。所以我现在想到了 3 个意见:

  1. 可以将编程语言比作具有动量的对象。用户群是它的质量和对其施加影响的风格。最初的用户/开发人员可以将一种语言拉向某个方向,因为它的质量仍然很低。即使非常流行(例如 C/C++),一门语言仍然可以发展,但它更难。 Julia 的未来仍未成文,但它的承诺正朝着其创造者和早期用户所灌输的最初方向发展。

  2. 最好延迟优化,直到正确性得到充分测试。 “过早的优化是万恶之源”(D. Knuth)。再次记住这一点永远不会伤害。因此,在可能混淆代码的边界区域的优化阶段之前,保持代码的可读性和正确性会更好。

  3. 表达式sum([x*y ...]) 可能需要编译器太聪明,最好简单定义一个sumprod(x,y)。这将允许 sumprod 利用 Julia 的通用函数多调度框架并针对 xy 保持优化,并且可能在以后针对特定类型的 xy 进行更多优化。

    李>

到此为止。意见很多。所以让我们继续讨论吧。

【讨论】:

    【解决方案3】:

    背景:我是一名 R/Rcpp 程序员,已经使用 Julia 大约一个月了。我编写的 R 代码至少有一半调用了我编写的 C++ 代码。

    结论:我认为 Julia 解决了这两种编程语言的问题是可能的。这并不意味着可以像编写原生 R/Python 一样编写高性能代码,但它大大降低了编写易于他人使用的高性能代码所需的工作量。

    进一步讨论:首先,我认为编写高性能代码根本不可能不考虑 C/C++ 类型问题,即声明类型,考虑有效的数据结构而不是可读的数据结构等。所以我认为,如果人们希望他们将拥有一种不需要关心数据结构的高性能语言,那么他们就不走运了。当然,我是统计学家而不是 CS 人,所以我承认我在这里可能是错的。

    但是,我有点惊讶于使用 Julia 似乎减少了我的工作时间。特别是,我的很多工作都涉及首先为一些计算密集的部分编写一些 C++ 代码。一旦启动并运行,我基本上是在本机 R 中编写 R 包装器和数据处理器,为我的 C++ 代码准备数据。预处理通常在计算上并不昂贵,并且 R 有很多不错的工具可以用来做这件事。如果一切都提前精心计划,这在理论上并不是一个糟糕的过程。

    然而,实际上,实际发生的情况是我让所有东西都启动并运行,然后我意识到我出于某种原因想要更改一些 C++ 代码。这就是令人头疼的地方。不幸的是,在这一点上,我经常得到所有数据的两个版本。一个将事物传递给一堆本地 R 预处理工具,另一个将事物作为 C++ 对象传递。与一些 C++ 对象混淆可能会对所有 R 对象产生很多后果。老实说,我浪费了太多时间试图解决这一切。

    到目前为止,我对 Julia 的体验是,这种头痛已经消失了。我正在使用的数据不再有 R 版本和 C++ 版本。只是朱莉娅。这也意味着很容易将其他人的代码引入我的代码的高性能部分。就像最近的一个例子一样,我一直想使用 Matern 协方差函数。这是一个重要的功能,我不想自己实现它。我可以找到很多拥有 R 包的人,这些 R 包是用原生 R 中很好的、有据可查的调用实现的。但是,就我而言,我真的很想从 C++ 调用它,而不需要所有原生 R 开销。这意味着(a)我需要搜索每个人不一定有充分的文档记录,也不一定需要在他们的 R 包中搜索用户友好的 C++ 代码,或者(b)重新发明轮子。另一方面,如果我使用的是 Julia,应该是如果有人编写了一个很好的 Matern 协方差函数供世界使用,我应该能够直接使用它,而不是在他们的代码中为调用 I 其实想用。

    当我开始构建更复杂的 Julia 项目时,我可能会发现一些我还没有意识到的类似麻烦事。但到目前为止,它似乎确实改进了将易于使用的工具(即 R、类似 Python 的调用)与高性能代码相结合的过程。

    【讨论】:

      【解决方案4】:

      您能否编写一个为您抽象出这些概念的类(即 FastDict 或 FastList 或其他)?然后,您将拥有相同的易读性(如果有点像黑盒),同时拥有高性能的代码。

      【讨论】:

      • 实际上是的,除了方法 [不是类,Julia 将其称为“类型”作为类型仅“拥有”数据,而不是方法],通过根据低级方法编写更高级别的方法这个苦差事。我只是担心我正在重新发明轮子的可能性。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-02-04
      • 1970-01-01
      • 1970-01-01
      • 2021-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多