Clojure的视频数据的性能问题

问题描述 投票:4回答:2

我正在写一些代码来生成和处理大量的视频数据。起初我只打算用随机的数据进行工作。

我的技术是治疗的像素作为地图上的R,G,B,A的整数值的,以治疗视频作为这些像素图的矢量的帧,并且在时间上处理视频作为像素映射的这些矢量的矢量。我已经写了三个函数做到这一点可靠,但我遇到了性能问题,他们缩放时。

(defn generateFrameOfRandomVideoData
  "Generates a frame of video data which is a vector of maps of pixel values."
  [num-pixels-in-frame]
  (loop [num-pixels-in-frame num-pixels-in-frame
     pixels-added 0
     frame '[]]
(if (> num-pixels-in-frame pixels-added)
 (recur num-pixels-in-frame
        (inc pixels-added) 
        (conj frame (assoc '{} 
                           :r (rand-int 256)
                           :g (rand-int 256)
                           :b (rand-int 256)
                           :a (rand-int 256))))
 frame)))

(defn generateRandomVideoData
   "Generates a vector of frames of video data."
   [number-of-frames frame-height frame-width]
   (loop [number-of-frames number-of-frames
     frame-height frame-height
     frame-width frame-width
     frames '[]]
(if (> number-of-frames (count frames))
 (recur number-of-frames
        frame-height
        frame-width
        (conj frames (generateFrameOfRandomVideoData (* frame-height frame-width))))
 frames)))

 (defn generateRandomizedVideo
 "Generates video data based on the specified parameters."
 [number-of-frames frame-height frame-width]
    (assoc '{} 
     :number-of-frames number-of-frames
     :frame-height frame-height
     :frame-width frame-width
     :frames (generateRandomVideoData number-of-frames frame-height frame-width)))

调用此使用函数产生1920x1080像素的视频60帧:

(generateRandomizedVideo 60 1920 1080)

当我运行这个调用产生10帧值得1920x1080像素的视频算法很快完成。当我把它制作视频的60帧它停滞不前,无法完成,并产生大量内存。我看着它占用的内存16GB的价值。

这并不真正使任何意义,我。我的算法是O(帧*(帧的帧*宽度)的高度的数量)。帧的数量是O(n)和帧*(高度帧的宽度将在O常数(高度×宽)。这些参数解析为O(n)中。

现在,我已经说服自己,希望你,我的算法是不只是顽固,我觉得我有一些连贯的问题:

  1. 多少内存Clojure中的一个整数占用的位?我似乎无法在任何地方找到这些信息。
  2. 什么样的开销也储运整数势必映射键原因在哪里?它是在记忆不仅仅是让他们在一个载体而言更昂贵?
  3. 为什么算法陷入泥淖中的时间和内存的大量帧的条款?什么是Clojure的做霸占这么多内存?

谢谢!

clojure jvm video-processing
2个回答
7
投票

多少内存Clojure中的一个整数占用的位?

16个字节,根据clj-memory-meter

(mem/measure (rand-int 256))
=> "16 B"

只有4个字节被用于表示一个32位的整数的值,但在一个的Clojure是java.lang.Integer中的相同Java和有额外的存储空间“开销”每java.lang.Object

(type (rand-int 256))
 => java.lang.Integer

什么样的开销也储运整数势必映射键原因在哪里?它是在记忆不仅仅是让他们在一个载体而言更昂贵?

是的,几乎两倍在这种情况下:

(mem/measure [(rand-int 256) (rand-int 256) (rand-int 256) (rand-int 256)])
=> "320 B"
(mem/measure {:r (rand-int 256)
              :g (rand-int 256)
              :b (rand-int 256)
              :a (rand-int 256)})
=> "544 B"

每一帧将是相当大的:

(mem/measure
  (into [] (repeatedly (* 1920 1080)
                       (fn [] {:r (rand-int 256)
                               :g (rand-int 256)
                               :b (rand-int 256)
                               :a (rand-int 256)}))))
 => "232.2 MB"

为什么算法陷入泥淖中的时间和内存的大量帧的条款?什么是Clojure的做霸占这么多内存?

存储每个像素的哈希地图将会加起来非常快,如果每个1920×1080帧〜232 MB这是〜1 GB每4帧。我不认为这是特定的Clojure - 这是任何语言昂贵的存储方案。我会考虑几件事情:

  • 更有效地存储各个像素值例如表示每个像素四个无符号字节打包成一个单一的32位整数。一个开放的哈希地图可能是最节省空间的结构之一,当你有这么多的数据点,都在相同的结构。 由于您的地图形状明确定义,你可以使用一个记录,以节约空间,并有地图样的语义: (defrecord Pixel [r g b a]) (mem/measure (->Pixel (rand-int 256) (rand-int 256) (rand-int 256) (rand-int 256))) => "112 B" ;; similar deftype is 96 B 四个甲原始整数数组只比单个Integer对象稍大: (mem/measure (int-array (range 4))) => "32 B" 类似的载体是10倍大的: (mem/measure [(int 0) (int 1) (int 2) (int 3)]) => "320 B" 你可以尝试字节数组,但JVM没有无符号字节原语: (mem/measure (byte-array 4)) => "24 B"
  • 有很多的不可变的数据结构变化发生的每个像素和帧是越来越conj'd到现有的载体,并且不来“免费”使用Clojure的持久数据结构。更有效的方式来做到这一点是使用transients,但...
  • 你需要存储在内存中的所有这些帧?如果没有,你可以未持有他们都懒洋洋地流这些。如果你将其纳入一个大的,实现了采集,也许还可以利用瞬变,JVM阵列等。 (defn gen-frame [num-pixels] (repeatedly num-pixels #(->Pixel (rand-int 256) (rand-int 256) (rand-int 256) (rand-int 256)))) (defn frame-op [frame] ;; not very interesting for random pixels (let [num-pixels (count frame) avg #(double (/ (apply + (map % frame)) num-pixels))] (->Pixel (avg :r) (avg :g) (avg :b) (avg :a)))) (time (->> (repeatedly #(gen-frame (* 1920 1080))) (map frame-op) (take 60) (doall))) "Elapsed time: 240527.803662 msecs" => (#sandbox.core.Pixel{:r 127.4540152391975, :g 127.4542722800926, :b 127.3754962384259, :a 127.4886294367284} #sandbox.core.Pixel{:r 127.4727488425926, :g 127.4447955246914, :b 127.4472164351852, :a 127.4626080246914} ... 这个例子是懒惰地分析一个无限序列的每个帧并取第一60点的结果;所分析的帧/像素数据是越来越收集垃圾,因为这运行,所以不会出的存储器中运行时(但GC将忙于)。

这些参数解析为O(N)。

大常数关系,有时!


0
投票

如果您需要什么,你可以走出@Taylor伍德回答的进一步加速,可以考虑进一步压缩存储。

如果你只是按99,Clojure的将存储作为java.lang.Long,占用数每64个字节。使用java.lang.Integer将削减在一半,占用了每号32个字节。

但是,我们有进一步优化的余地!你0和255之间生成数字,这意味着你需要每数量的存储log2(256) = 8位。然后,我们可以容纳所有的三个RGB值到一个单一的java.lang.Integer

我下面就开始了。这种方法学分转到mikera/imagez。如果你喜欢扭捏多了,可以尽量避免我remquot的使用,去摆弄位代替。内存将是相同的,但CPU使用率会下降。

(defn encodable? [i]
  (and (nat-int? i)
       (< i 256)))

(defn rgb->int
  "Store an RGB value in a single integer!"
  [[r g b]]
  (do (assert (encodable-int? r))
      (assert (encodable-int? g))
      (assert (encodable-int? b)))
  (int
   (+ (* 256 256 r)
      (* 256 g)
      b)))

(defn int->rbg [i]
  [(rem (quot i (* 256 256)) 256)
   (rem (quot i 256) 256)
   (rem i 256)])

;; Let's try to store 99, 101, 255!

(def c [99 101 255])

(rgb->int c)
;; => 6514175

(type (rgb->int c))
;; => java.lang.Integer

(-> c rgb->int int->rbg)
;; => [99 101 255]
© www.soinside.com 2019 - 2024. All rights reserved.