为什么我的Clojure项目在树莓派上运行缓慢?

5
我一直在为我的 Raspberry Pi 编写一个简单的 Clojure 框架,用于播放音乐(以及稍后的其他东西)。该程序会解析给定音乐目录中的歌曲,然后通过 TCP 接口开始等待控制命令(例如开始、停止、下一首歌曲)。
该代码可通过 GitHub 获取: https://github.com/jvnn/raspi-framework 当前版本在我的笔记本电脑上运行良好,当指示时可以开始播放音乐(使用 JLayer 库),更换歌曲,并像预期那样停止。超级jar文件在笔记本电脑上需要几秒钟才能启动,但是当我尝试在 Raspberry Pi 上运行它时,速度变得非常慢。
只是启动程序以加载所有类并开始执行实际程序代码就需要超过一分钟。我尝试使用 -verbose:class 开关运行它,似乎 jvm 花费了整个时间来加载大量类(针对 Clojure 和其他所有内容)。
当程序最终启动时,确实会对给定的命令做出反应,但回放非常卡顿。有短暂的次秒声音,然后暂停了几乎一秒钟,再播放另一个声音,再暂停等等......因此,该程序正在尝试播放某些内容,但它只是无法快速做到。CPU 使用率接近 98%。
现在,拥有 Android 手机等一切,我相信可以在这种硬件上很好地执行 Java 来播放一些 mp3 文件而没有任何问题。我知道 JLayer(或其中的一部分)用于 gdx 游戏开发框架(也在 Android 上运行),因此这不应该是问题。
所以一切都指向我是问题所在。是否有什么我可以通过 leiningen(对所有文件已启用 aot)、Raspberry Pi 或我的代码来使事情更快的方法?
感谢您的时间!
更新: 我制作了一个小测试用例来排除一些可能性,但以下 Clojure 代码仍存在问题:
(ns test.core
  (:import [javazoom.jl.player.advanced AdvancedPlayer])
  (:gen-class))

(defn -main
  []
  (let [filename "/path/to/a/music/file.mp3"
        fis (java.io.FileInputStream. filename)
        bis (java.io.BufferedInputStream. fis)
        player (AdvancedPlayer. bis)]
    (doto player (.play) (.close))))

项目.clj文件:
(defproject test "0.0.1-SNAPSHOT"
  :description "FIXME: write description"
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [javazoom/jlayer "1.0.1"]]
  :javac-options ["-target" "1.6" "-source" "1.6" "-Xlint:-options"]
  :aot :all
  :main test.core)

所以,没有核心异步和线程。播放流畅了一些,但音乐和暂停之间仍有大约200毫秒的间隔。


我认为这可能与树莓派CPU的较小缓存大小有关。Clojure很棒,但惯用的Clojure在缓存翻转方面比大多数其他语言要求更高(特别是因为它大量使用不可变数据结构)。 - noisesmith
我认为这并不能解释播放问题(也不是缓慢的启动时间)。因为(至少如果代码按照我的意图工作),在由Java库处理的播放期间,所有Clojure代码都应该被停止并等待某些异步通道输入。 - J.Nieminen
那个停车场可能涉及到一些上下文切换。上下文切换会破坏你的缓存,这是很昂贵的(特别是在总线速度较慢的情况下)。一旦你拥有了一个媒体流,它直接从RAM(甚至磁盘)到输出设备而无需修改,导致其跳过的东西就是上下文切换,而上下文切换所需的缓存越多,跳过问题就越严重。 - noisesmith
我使用了一个最小的测试案例更新了问题。我添加的代码是目前唯一运行的东西,但还远不够顺畅。我也检查了 Pi 上的声音是否正常工作,通过使用 gstreamer 播放音乐文件,它没有出现问题。 - J.Nieminen
如果那段代码跳过了,那么问题要么是javazoom类,要么是JVM。我正在写一个答案,但是我查看了javazoom文档,没有找到关于输出设备缓冲区大小的选项。如果它不允许你调整缓冲区大小,你应该使用另一个库(考虑到这段代码已经很简化了,除了改变输出缓冲区大小之外,我无法想象还有什么能提高性能)。 - noisesmith
显示剩余2条评论
1个回答

5

我认为最明显的问题是你有很多未提示的Interop代码,导致运行时反射非常昂贵。尝试运行lein check(我觉得这是内置的,但也许你需要一个插件),并修复它指出的反射问题。


感谢你的回答。虽然lein check没有提示我任何信息,它只输出了“Compiling namespace...”。但是我想,即使反射在运行时让程序变慢了,它也不能成为长时间启动的原因吧? - J.Nieminen
重新启动时间...Clojure很大,而树莓派很小。如果您在启动时看到了大量时间用于加载类,那可能是因为有许多类需要加载。我想象您的jar文件相对于树莓派想要看到的内容来说相当沉重。 - amalloy
好的,这正是我害怕听到的。我希望有一些可以做的事情(优化、不同的Java版本等)来改善情况。 - J.Nieminen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接