- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在编写一些代码来生成和处理大量视频数据。起初我只想使用随机数据。
我的技术是将像素视为 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 中的整数占用多少位内存?我似乎无法在任何地方找到此信息。
存储绑定(bind)到映射键的整数会导致什么样的开销?就内存而言,是否比将它们保存在向量中更昂贵?
为什么算法在处理大量帧时会在时间和内存方面陷入困境? Clojure 到底在做什么,占用了这么多内存?
谢谢!
最佳答案
How much memory does an integer in Clojure take up in bits?
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
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?
如果每个 1920x1080 帧约为 232 MB,那么每 4 帧约为 1 GB,那么每个像素存储一个 HashMap 将会很快增加。我不认为这是 Clojure 特有的——对于任何语言来说,这都是一个昂贵的存储方案。我会考虑以下几点:
更有效地存储各个像素值,例如将每个像素表示为打包成单个 32 位整数的四个无符号字节。当您拥有这么多数据点且全部位于同一结构中时,开放 HashMap 可能是空间效率最低的结构之一。
由于您的 map 形状已明确定义,因此您可以使用记录来节省空间并具有类似 map 的语义:
(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
到现有向量的情况下,会发生很多不可变的数据结构变化,而这并不是“免费”的使用 Clojure 的持久数据结构。更有效的方法是使用 transients ,但是……
您需要将所有这些帧存储在内存中吗?如果没有,您可以懒惰地流式传输这些内容,而无需将它们全部保留。如果您必须将它们构建到一个大型的、已实现的集合中,也许可以使用 transient 、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).
有时大常数很重要!
关于Clojure 视频数据性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54526425/
为什么该语言的名称是“Clojure”? 我用谷歌搜索了一下,在#clojure 中询问。到目前为止,还没有运气。 最佳答案 Rich Hickey(他是 Clojure 的设计者)对此的评论是 wi
我不明白为什么升级后会出现以下编译错误: Compiling addr-verify.core Exception in thread "main" java.lang.NoClassDefFound
我试图将从映射操作返回的(惰性)序列传递给另一个映射操作,以便我可以在第一个序列中查找元素。代码从文本文件(以行/列格式)解析一些足球装置,清理它,然后返回一张 map 。 这是代码: (ns fix
我想过滤一组,例如: (filter-set even? #{1 2 3 4 5}) ; => #{2 4} 如果我使用clojure.core/filter我得到一个不是集合的seq: (filte
(defn hi[](+ 5 6)) (hi) (defn hi[](+ 6 7)) (hi) 你好,我是 clojure 的新手。如上所述,我编写了两个具有相同名称的函数。我们可以在 cloj
我按照这个伪代码递归地将十进制转换为二进制。 findBinary(decimal) if (decimal == 0) binary = 0 else binar
我正在尝试学习 Clojure 并尝试定义这个简单的函数: user=> (defn triple [arg] (* 3 arg)) #'user/triple user=> (triple 1) 3
是->和 ->>宏只是为了使代码更具可读性还是它们还有其他特定功能? 最佳答案 线程优先( -> )和线程最后( ->> )是为了使代码更具可读性。但这已经很重要了! 它允许取消嵌套函数调用(示例取自
我在 http://www.learningclojure.com/2010/11/yet-another-way-to-write-factorial.html 上找到了这个代码,但我不明白 pop
我正在阅读 Programming Clojure 2nd edition,在第 49 页它涵盖了 Clojure 的 for 循环结构,它说它实际上是一个序列理解。 作者建议使用以下代码: (def
Clojure 中有双端队列吗?我的印象是 Clojure 的 PersistentQueue 是单端的(我错了吗?)。我需要能够从队列的任一端删除(即“pop”)和“peek”数据。我所说的双端队列
换句话说,有没有办法在看起来不像 (MACRO arg* ...) 的表单上触发宏扩展? . 举一个假设的例子: (defmacro my-var (do (printf "Using my-va
我很难理解懒惰。 有人能帮我理解为什么我下面的函数不是懒惰的吗 (defn my-red ([f coll] (my-red f (first coll) (rest coll) ))
在 Clojure 核心中决定参数函数顺序的规则是什么(如果有的话)? 类似 map 的函数和 filter期望数据结构作为最后一个 争论。 类似 assoc 的函数和 select-keys期待数据
我在 clojuredocs 上遇到过 completing 函数,但目前没有文档。 你能提供一些例子吗? 最佳答案 completing 用于扩充可能没有具有一元“完成”元数的一元重载的二元归约函数
这个现在支持吗?我能找到的唯一信息是来自维基的示例( https://github.com/clojure/core.match/wiki/Deftype-and-defrecord-matching
我正在关注“Clojure in Action”,对此我感到困惑: (defn with-log [function-to-call log-statement ] (fn [& args
对于下面的代码,箭头是宏还是函数名称中的简单字符? (来自 here) (defn file->map [file] ;; TODO ) 最佳答案 箭头是函数名称的一部分。有一个函数定义,不是
Clojure 的 range函数包含来自 start独家在end (如果提供)。核心库中是否有一个函数可以提供完全包含(开始和结束)的范围? 我发现在某些情况下必须调整最终值的代码 - 例如向下而不
当我尝试从 REPL 运行以下代码时(使用动态记录): (defrecord (symbol "rec2") (vec (map symbol ["f1" "f2"]))) 我收到错误 Compile
我是一名优秀的程序员,十分优秀!