提高 Clojure 中点云边界框计算的性能
Improve performance of point cloud bounding box computation in Clojure
我正在用 Clojure 计算 3d 点云的边界框。点云表示为 Java 原始浮点数组,点云中的每个点都使用 4 个浮点数存储,其中最后一个浮点数未使用。像这样:
[x0 y0 z0 u0 x1 y1 z1 u1 .... ]
点云大小为19200,即数组中有4*19200个浮点数
其中一些值可能不是有限的(它们要么是无限的,要么是 NaN)。因此,任何包含非有限值的点都应完全排除在计算之外。我已经在 Java 和 Clojure 中实现了这个计算,但出于某种原因,Clojure 版本仍然慢了大约 4 到 5 倍。
Clojure 代码如下所示:
(defn good-compute-bbox [^floats data]
(let [n (alength data)]
(loop [i (int 0)
minx (float (aget data 0))
maxx (float (aget data 0))
miny (float (aget data 1))
maxy (float (aget data 1))
minz (float (aget data 2))
maxz (float (aget data 2))]
(if (< i n)
(let [x (float (aget data (unchecked-add i 0)))
y (float (aget data (unchecked-add i 1)))
z (float (aget data (unchecked-add i 2)))]
(if (and (Float/isFinite x)
(Float/isFinite y)
(Float/isFinite z))
(recur (unchecked-add-int i (int 4))
(min minx x)
(max maxx x)
(min miny y)
(max maxy y)
(min minz z)
(max maxz z))
(recur (unchecked-add-int i (int 4))
minx
maxx
miny
maxy
minz
maxz
))
)
[minx maxx miny maxy minz maxz]))))
这就是 Java 代码的样子:
public class BBox {
public static float[] computeBBox(float[] data) {
long n = data.length;
float[] result = new float[6];
float minx = data[0];
float maxx = data[0];
float miny = data[1];
float maxy = data[1];
float minz = data[2];
float maxz = data[2];
for (int i = 0; i < n; i += 4) {
float x = data[i + 0];
float y = data[i + 1];
float z = data[i + 2];
if (java.lang.Float.isFinite(x) &&
java.lang.Float.isFinite(y) &&
java.lang.Float.isFinite(z)) {
minx = x < minx? x : minx;
maxx = x > maxx? x : maxx;
miny = y < miny? y : miny;
maxy = y > maxy? y : maxy;
minz = z < minz? z : minz;
maxz = z > maxz? z : maxz;
}
}
result[0] = minx;
result[1] = maxx;
result[2] = miny;
result[3] = maxy;
result[4] = minz;
result[5] = maxz;
return result;
}
};
我的问题是: 我可以对我的 Clojure 代码进行哪些更改以使其 运行 与 Java 代码一样快?如果您测试了您的代码并测量了加速,则可加分。
如果你想重现实验和published on my Github repository here。
这里的罪魁祸首是 min
,正如我从您的代码中看出您已经怀疑的那样。它适用于各种类型,但并不是那么快。
通过使用 Apache 的通用快速数学,您可以获得 Java 的 10% 以内:
(defn primitive-compute-bbox-new [^floats data]
(let [n (alength data)]
(loop [i (int 0)
minx (aget data 0)
maxx (aget data 0)
miny (aget data 1)
maxy (aget data 1)
minz (aget data 2)
maxz (aget data 2)]
(if (< i n)
(let [x (aget data (unchecked-add i 0))
y (aget data (unchecked-add i 1))
z (aget data (unchecked-add i 2))]
(if (and (Float/isFinite x)
(Float/isFinite y)
(Float/isFinite z))
(recur (unchecked-add-int i 4)
(FastMath/min (float minx) x)
(FastMath/max (float maxx) x)
(FastMath/min (float miny) y)
(FastMath/max (float maxy) y)
(FastMath/min (float minz) z)
(FastMath/max (float maxz) z))
(recur (unchecked-add-int i 4)
minx
maxx
miny
maxy
minz
maxz)))
[minx maxx miny maxy minz maxz]))))
部门:
[org.apache.commons/commons-math3 "3.6.1"]
我正在用 Clojure 计算 3d 点云的边界框。点云表示为 Java 原始浮点数组,点云中的每个点都使用 4 个浮点数存储,其中最后一个浮点数未使用。像这样:
[x0 y0 z0 u0 x1 y1 z1 u1 .... ]
点云大小为19200,即数组中有4*19200个浮点数
其中一些值可能不是有限的(它们要么是无限的,要么是 NaN)。因此,任何包含非有限值的点都应完全排除在计算之外。我已经在 Java 和 Clojure 中实现了这个计算,但出于某种原因,Clojure 版本仍然慢了大约 4 到 5 倍。
Clojure 代码如下所示:
(defn good-compute-bbox [^floats data]
(let [n (alength data)]
(loop [i (int 0)
minx (float (aget data 0))
maxx (float (aget data 0))
miny (float (aget data 1))
maxy (float (aget data 1))
minz (float (aget data 2))
maxz (float (aget data 2))]
(if (< i n)
(let [x (float (aget data (unchecked-add i 0)))
y (float (aget data (unchecked-add i 1)))
z (float (aget data (unchecked-add i 2)))]
(if (and (Float/isFinite x)
(Float/isFinite y)
(Float/isFinite z))
(recur (unchecked-add-int i (int 4))
(min minx x)
(max maxx x)
(min miny y)
(max maxy y)
(min minz z)
(max maxz z))
(recur (unchecked-add-int i (int 4))
minx
maxx
miny
maxy
minz
maxz
))
)
[minx maxx miny maxy minz maxz]))))
这就是 Java 代码的样子:
public class BBox {
public static float[] computeBBox(float[] data) {
long n = data.length;
float[] result = new float[6];
float minx = data[0];
float maxx = data[0];
float miny = data[1];
float maxy = data[1];
float minz = data[2];
float maxz = data[2];
for (int i = 0; i < n; i += 4) {
float x = data[i + 0];
float y = data[i + 1];
float z = data[i + 2];
if (java.lang.Float.isFinite(x) &&
java.lang.Float.isFinite(y) &&
java.lang.Float.isFinite(z)) {
minx = x < minx? x : minx;
maxx = x > maxx? x : maxx;
miny = y < miny? y : miny;
maxy = y > maxy? y : maxy;
minz = z < minz? z : minz;
maxz = z > maxz? z : maxz;
}
}
result[0] = minx;
result[1] = maxx;
result[2] = miny;
result[3] = maxy;
result[4] = minz;
result[5] = maxz;
return result;
}
};
我的问题是: 我可以对我的 Clojure 代码进行哪些更改以使其 运行 与 Java 代码一样快?如果您测试了您的代码并测量了加速,则可加分。
如果你想重现实验和published on my Github repository here。
这里的罪魁祸首是 min
,正如我从您的代码中看出您已经怀疑的那样。它适用于各种类型,但并不是那么快。
通过使用 Apache 的通用快速数学,您可以获得 Java 的 10% 以内:
(defn primitive-compute-bbox-new [^floats data]
(let [n (alength data)]
(loop [i (int 0)
minx (aget data 0)
maxx (aget data 0)
miny (aget data 1)
maxy (aget data 1)
minz (aget data 2)
maxz (aget data 2)]
(if (< i n)
(let [x (aget data (unchecked-add i 0))
y (aget data (unchecked-add i 1))
z (aget data (unchecked-add i 2))]
(if (and (Float/isFinite x)
(Float/isFinite y)
(Float/isFinite z))
(recur (unchecked-add-int i 4)
(FastMath/min (float minx) x)
(FastMath/max (float maxx) x)
(FastMath/min (float miny) y)
(FastMath/max (float maxy) y)
(FastMath/min (float minz) z)
(FastMath/max (float maxz) z))
(recur (unchecked-add-int i 4)
minx
maxx
miny
maxy
minz
maxz)))
[minx maxx miny maxy minz maxz]))))
部门:
[org.apache.commons/commons-math3 "3.6.1"]