Clojure视频数据性能问题

Clojure视频数据性能问题,clojure,jvm,video-processing,Clojure,Jvm,Video Processing,我正在写一些代码来生成和处理大量的视频数据。起初,我只打算处理随机数据 我的技术是将像素视为R、G、B、整数值的映射,将视频帧视为这些像素映射的向量,并将跨时间的视频视为这些像素映射向量的向量。我已经编写了三个函数,它们可以可靠地实现这一点,但在扩展时会遇到性能问题 (defn generateFrameOfRandomVideoData "Generates a frame of video data which is a vector of maps of pixel values."

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

我的技术是将像素视为R、G、B、整数值的映射,将视频帧视为这些像素映射的向量,并将跨时间的视频视为这些像素映射向量的向量。我已经编写了三个函数,它们可以可靠地实现这一点,但在扩展时会遇到性能问题

(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做了什么来占用这么多内存

  • 谢谢

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

    16字节,根据:

    只有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做了什么来占用这么多内存

    每像素存储一个哈希图会很快加起来,如果每个1920x1080帧都是232 MB,那么每4帧就有1 GB。我不认为这是对Culjule-这是一种昂贵的任何语言的存储方案。我会考虑一些事情:

    • 更有效地存储单个像素值,例如,将每个像素表示为压缩为单个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
      
      四个基本整数数组仅略大于单个
      整数
      对象:

      (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的持久数据结构并不是“免费”的。更有效的方法是使用,但是

    • 您需要将所有这些帧存储在内存中吗?如果不需要,您可以在不保存所有帧的情况下延迟流式处理这些帧。如果您必须将它们构建到一个大型的、已实现的集合中,可以使用瞬态、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)


    如果你需要从Taylor Wood的答案中得到进一步的加速,考虑进一步压缩你的存储空间。

    如果您只需按99,Clojure会将其存储为
    java.lang.Long
    ,每个数字占用64字节。使用
    java.lang.Integer
    会将其减半,每个数字占用32字节

    但我们还有进一步的优化空间!您生成的数字介于0和255之间,这意味着您需要
    log2(256)=8位用于每个数字的存储。然后,我们可以将所有三个RGB值放入一个
    java.lang.Integer

    我从下面开始。这种方法的功劳是。如果你想调整更多,你可以尝试避免我使用
    rem
    quot
    而改为使用位摆弄。内存将是相同的,但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]
    
    (defn可编码?[i]
    (和(nat int?i)
    (int
    “将RGB值存储在单个整数中!”
    [[RGB]]
    (do(断言(可编码int?r))
    (断言(可编码int?g))
    (断言(可编码int?b)))
    (int)
    (+(*256 r)
    (*256克)
    b) ))
    (定义int->rbg[i]
    [(rem(QUOTE i(*256))256)
    (rem(QUOTE i 256)256)
    (rem i 256)])
    让我们试着存储99101255!
    (def c[99 101 255])
    (rgb->INTC)
    ;; => 6514175
    (类型(rgb->INTC))
    ;;=>java.lang.Integer
    (->c rgb->int int->rbg)
    ;; => [99 101 255]
    
    这都是很好的分析,但我认为最后一个问题没有得到足够的重视。即使是这种非常臃肿的存储格式,如果您懒散地处理它,也是完全可以管理的,对于大多数应用程序来说,懒散也没问题:将每个帧写入磁盘,然后扔掉,或者其他什么。总体回答很好。您检查了内存多少了吗如果为每个像素使用一条记录,则使用y?可能是对您的答案的一个很好的补充。T
    (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]