当前位置 : 主页 > 网络安全 > 测试自动化 >

Clojure视频数据性能问题

来源:互联网 收集:自由互联 发布时间:2021-06-22
我正在编写一些代码来生成和处理大量的视频数据.起初我打算只处理随机数据. 我的技术是将像素视为R,G,B,A整数值的映射,将视频帧视为这些像素映射的矢量,并将视频随时间视为像素映
我正在编写一些代码来生成和处理大量的视频数据.起初我打算只处理随机数据.

我的技术是将像素视为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)))

调用此方法可以使用这些函数生成60帧1920X1080p视频:

(generateRandomizedVideo 60 1920 1080)

当我运行此调用以生成10帧1920X1080p视频时,算法很快完成.当我把它称为生成60帧视频时,它会陷入困境,无法完成,并产生大量内存.我看着它占用了16GB的内存.

这对我来说没有任何意义.我的算法是O(帧数*(帧的高度*帧的宽度)).帧数是O(n)和(帧的高度*帧的宽度在O(高度*宽度)处是恒定的.这些参数解析为O(n).

既然我已经说服了自己,并希望你我的算法不仅仅是难以解决的问题,我想我有一些连贯的问题:

> Clojure中的整数占用多少内存?我似乎无法在任何地方找到这些信息.
>存储整数绑定到映射键的原因是什么?在内存方面是否比仅仅将它们保存在向量中更昂贵?
>为什么算法会因大量帧的时间和内存而停滞不前?什么是Clojure为了记住这么多记忆而做的事情?

谢谢!

How much memory does an integer in Clojure take up in bits?

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

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

只有4个字节用于表示32位整数值,但Clojure中的java.lang.Integer与Java中的相同,并且每个java.lang.Object都有额外的存储“开销”:

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

What kind of overhead does storing Integers bound to map keys cause? Is it costlier in terms of memory than just keeping them in a vector?

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

(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"

Why is the algorithm bogging down in terms of time and memory for large numbers of frames? What is Clojure doing to hog so much memory?

如果每个1920×1080帧为~232 MB,即每4帧约1 GB,则每个像素存储哈希映射将非常快速地加起来.我认为这不是特定于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"

>发生了很多不可变的数据结构变化,每个像素和帧都会在现有的矢量上结合,而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会忙).

These arguments resolve to O(n).

大常数很重要,有时候!

网友评论