- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
对于一个研究项目,我需要计算很多欧几里得距离,其中必须选择某些尺寸而将其他尺寸丢弃。在程序的当前状态下,选定维度的数组包含100个ish元素,我计算出大约2-3百万的距离。我当前的代码如下:
float compute_distance(const float* p1, const float* p2) const
{
__m256 euclidean = _mm256_setzero_ps();
const uint16_t n = nbr_dimensions;
const uint16_t aligend_n = n - n % 16;
const float* local_selected = selected_dimensions;
for (uint16_t i = 0; i < aligend_n; i += 16)
{
const __m256 r1 = _mm256_sub_ps(_mm256_load_ps(&p1[i]), _mm256_load_ps(&p2[i]));
euclidean = _mm256_fmadd_ps(_mm256_mul_ps(r1, r1), _mm256_load_ps(&local_selected[i]), euclidean);
const __m256 r2 = _mm256_sub_ps(_mm256_load_ps(&p1[i + 8]), _mm256_load_ps(&p2[i + 8]));
euclidean = _mm256_fmadd_ps(_mm256_mul_ps(r2, r2), _mm256_load_ps(&local_selected[i + 8]), euclidean);
}
float distance = hsum256_ps_avx(euclidean);
for (uint16_t i = aligend_n; i < n; ++i)
{
const float num = p1[i] - p2[i];
distance += num * num * local_selected[i];
}
return distance;
}
__m256
数组以传递给
_mm256_blendv_ps
,而不用在
euclidean = _mm256_fmadd_ps(_mm256_mul_ps(r1, r1), _mm256_load_ps(&local_selected[i]), euclidean);
行中乘以0或1。但是我不是内在指令的新手,而且还没有找到可行的解决方案。
float compute_distance(const float* p1, const float* p2) const
{
const size_t n = nbr_dimensions;
const size_t aligend_n = n - n % 16;
const unsigned int* local_selected = selected_dimensions;
const __m256* local_masks = masks;
__m256 euc1 = _mm256_setzero_ps(), euc2 = _mm256_setzero_ps(),
euc3 = _mm256_setzero_ps(), euc4 = _mm256_setzero_ps();
const size_t n_max = aligend_n/8;
for (size_t i = 0; i < n_max; i += 4)
{
const __m256 r1 = _mm256_sub_ps(_mm256_load_ps(&p1[i * 8 + 0]), _mm256_load_ps(&p2[i * 8 + 0]));
const __m256 r1_1 = _mm256_and_ps(r1, local_masks[i + 0]);
euc1 = _mm256_fmadd_ps(r1_1, r1_1, euc1);
const __m256 r2 = _mm256_sub_ps(_mm256_load_ps(&p1[i * 8 + 8]), _mm256_load_ps(&p2[i * 8 + 8]));
const __m256 r2_1 = _mm256_and_ps(r2, local_masks[i + 1]);
euc2 = _mm256_fmadd_ps(r2_1, r2_1, euc2);
const __m256 r3 = _mm256_sub_ps(_mm256_load_ps(&p1[i * 8 + 16]), _mm256_load_ps(&p2[i * 8 + 16]));
const __m256 r3_1 = _mm256_and_ps(r3, local_masks[i + 2]);
euc3 = _mm256_fmadd_ps(r3_1, r3_1, euc3);
const __m256 r4 = _mm256_sub_ps(_mm256_load_ps(&p1[i * 8 + 24]), _mm256_load_ps(&p2[i * 8 + 24]));
const __m256 r4_1 = _mm256_and_ps(r4, local_masks[i + 3]);
euc4 = _mm256_fmadd_ps(r4_1, r4_1, euc4);
}
float distance = hsum256_ps_avx(_mm256_add_ps(_mm256_add_ps(euc1, euc2), _mm256_add_ps(euc3, euc4)));
for (size_t i = aligend_n; i < n; ++i)
{
const float num = p1[i] - p2[i];
distance += num * num * local_selected[i];
}
return distance;
}
最佳答案
基本建议:
不要将uint16_t
用作循环计数器,除非您真的想强制编译器每次都将其截断为16位。至少使用unsigned
,否则有时您可以通过使用uintptr_t
(或更常见的是size_t
)获得更好的汇编。仅从使用32位操作数大小的asm指令就可以在x86-64上免费实现从32位到指针宽度的零扩展,但是有时候编译器仍然做不到。
使用五个或更多单独的累加器而不是一个euclidean
,因此可以运行多个sub / FMA指令,而不会阻塞将FMA集成到一个累加器中的循环依赖链的延迟。
FMA在Intel Haswell上具有5个周期的延迟,但每0.5个周期1个吞吐量。另请参阅latency vs throughput in intel intrinsics和我对Why does mulss take only 3 cycles on Haswell, different from Agner's instruction tables?的回答,以获取更高级的版本。
避免将args传递给全局变量。显然,您的n
是一个编译时常量(很好),但是selected_dimensions
不是,不是吗?如果是这样,那么您在整个程序中只使用一组掩码,因此不要忘记下面有关压缩掩码的内容。
当使用全局变量将函数内联到调用程序中时会破坏编译器的优化,该调用程序会在调用之前设置全局变量。 (通常仅在设置全局变量和使用全局变量之间存在非内联函数调用,但这并不少见。)
更新:您的数组很小,只有约100个元素,因此仅展开2个元素可能很好,以减少启动/清理开销。乱序执行可能会在这短暂的迭代中隐藏FMA延迟,尤其是在不需要此函数调用的最终结果来确定下一个调用的输入参数的情况下。
总的函数调用开销很重要,而不仅仅是大型数组矢量化的效率。
As discussed in comments,剥离循环的第一次迭代,可以通过初始化euc1 = stuff(p1[0], p2[0]);
而不是_mm256_setzero_ps()
来避免第一次FMA。
将数组填充为零的完整 vector (甚至是2个 vector 的完整展开循环体),可以完全避免标量清理循环,并使整个函数非常紧凑。
如果不能只是填充,您仍然可以通过加载一直到输入末尾的未对齐 vector 来避免标量清理,并对其进行掩码以避免重复计数。 (请参见this Q&A以获取基于未对齐计数生成蒙版的方法)。在编写输出数组的其他类型的问题中,可以重做重叠的元素。
您没有显示hsum256_ps_avx
代码,但这仅占总延迟以及函数吞吐量的一小部分。确保针对吞吐量进行优化:例如避免haddps
/ _mm_hadd_ps
。请参阅我对Fastest way to do horizontal float vector sum on x86的回答。
您的具体情况:
I could thus pre-compute an array of __m256 to pass to a
_mm256_blendv_ps
instead of multiplying by 0 or 1 in the FMA.
_mm256_and_ps
。这使值保持不变(
1 & x == x
)或清零(
0 & x == 0
,并且float
0.0
的二进制表示形式为全零。)
p1
和
p2
的不同蒙版,则可以预先计算平方的
p1-p2
,然后对此进行蒙版
add_ps
缩减。 (但请注意,在Intel Skylake之前,FMA具有比ADD更好的吞吐量。Haswell/ Broadwell有2个FMA单元,但在专用单元上运行ADDPS时延较低(3c对5c)。只有一个vector-FP加法单元。 Skylake只需在FMA单元上以4个周期的延迟运行所有内容。)无论如何,这意味着将FMA用作
1.0 * x + y
实际上可能是吞吐量的胜利。但是您可能还不错,因为您仍然需要分别加载掩码和
square(p1-p2)
,因此每个FP添加2次加载,因此每个周期进行一次加载就可以满足加载吞吐量。除非您(或编译器)在前面剥离一些迭代并将这些迭代的浮点数据保存在跨多个不同
local_selected
掩码的寄存器中。
pmovsx
( _mm256_cvtepi8_epi32
)加载它们(对全数值进行符号扩展会产生更宽的全数,因为这是2的补码
-1
的工作方式)。不幸的是,将其用作负载很烦人。编译器有时无法将
_mm256_cvtepi8_epi32(_mm_cvtsi64x_si128(foo))
优化为
vpmovsxbd ymm0, [mem]
,而是实际上使用单独的
vmovq
指令。
const uint64_t *local_selected = something; // packed to 1B per element
__m256 euc1 = _mm256_setzero_ps(), euc2 = _mm256_setzero_ps(),
euc3 = _mm256_setzero_ps(), euc4 = _mm256_setzero_ps();
for (i = 0 ; i < n ; i += 8*4) { // 8 floats * an unroll of 4
__m256 mask = _mm256_castsi256_ps( _mm256_cvtepi8_epi32(_mm_cvtsi64x_si128(local_selected[i*1 + 0])) );
// __m256 mask = _mm256_load_ps(local_selected[i*8 + 0]); // without packing
const __m256 r1 = _mm256_sub_ps(_mm256_load_ps(&p1[i*8 + 0]), _mm256_load_ps(&p2[i*8 + 0]));
r1 = _mm256_and_ps(r1, mask); // zero r1 or leave it untouched.
euc1 = _mm256_fmadd_ps(r1, r1, euc1); // euc1 += r1*r1
// ... same for r2 with local_selected[i + 1]
// and p1/p2[i*8 + 8]
// euc2 += (r2*r2) & mask2
// and again for euc3 (local_selected[i + 2], p1/p2[i*8 + 16]
// and again for euc3 (local_selected[i + 3], p1/p2[i*8 + 24]
}
euclidean = hsum (euc1+euc2+euc3+euc4);
pmovsx
的情况下会稍微影响瓶颈,因为对于三个 vector ALU操作,您有三个负载。 (使用微融合,在Intel CPU上总共只有4个融合域uops,因此在前端没有瓶颈)。而且这三个ALU操作系统可以在不同的端口上运行(对于Intel pre-Skylake上的端口5,
vandps
是1 uop。在SKL上,它可以在任何端口上运行)。
pmovsx
)可能存在的瓶颈。您可能想要使用
vpand
进行屏蔽,因此,即使您要调整HSW / BDW,即使它们在整数AND和FP数学指令之间有额外的旁路等待时间,它也可以在任何端口上运行。有了足够的累加器,您就不会受到延迟的限制。 (Skylake根据其运行在哪个端口上而为
VANDPS
提供了额外的旁路延迟)。
vblendvps
一起使用,以通过与0混合来有条件地将元素归零。 (或算术右移+ bool(boolean) AND)
__m256i masks = _mm256_load_si256(...);
// this actually needs a cast to __m256, omitted for readability
r0 = _mm256_blendv_ps(_mm256_setzero_ps(), r0, masks);
...
__m256i mask1 = _mm256_slli_epi32(masks, 1);
r1 = _mm256_blendv_ps(_mm256_setzero_ps(), r1, mask1);
...
__m256i mask2 = _mm256_slli_epi32(masks, 2);
r2 = _mm256_blendv_ps(_mm256_setzero_ps(), r2, mask2);
...
// fully unrolling is overkill; you can set up for a loop back to r0 with
masks = _mm256_slli_epi32(masks, 4);
masks = _mm256_slli_epi32(masks, 1);
,这可能会更好,因为它减少了1个寄存器的使用。但是,由于每个掩码都依赖于前一个掩码,因此它对引起掩码延迟链延迟的资源冲突可能更敏感。
vblendvps
uops,因此您可以考虑使用
_mm256_srai_epi32
+
_mm256_and_ps
。但是Skylake可以在p015的任何一个上运行2 oups,因此blend在那里很好(尽管它确实占用了保存全零 vector 的 vector 寄存器)。
_mm256_srli_epi32(cmp_result, 31)
和或与要构建的 vector 相乘。然后将其左移1。重复32次。
selected_dimensions
的掩码。例如每个元素的高16位用于一个
selected_dimensions
,低16位用于另一种。你可以做类似的事情
__m256i masks = _mm256_load_si256(dimensions[selector/2]);
masks = _mm256_sll_epi32(masks, 16 * (selector % 2));
// or maybe
if (selector % 2) {
masks = _mm256_slli_epi32(masks, 16);
}
const __mmask16 *local_selected = whatever;
声明一个16位掩码数组(用于16个浮点数的512b vector ),然后使用
r0 = _mm512_maskz_sub_ps(p1,p2, local_selected[i]);
对减法进行零掩码。
关于c++ - 欧氏距离使用内在指令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45735679/
A是不同元素的序列,B是A的子序列,A-B是A中的所有元素,但不是B中的所有元素距离(A) = 总和|a(i)-a(i+1)|从 i=1 到 n-1找到一个子序列 B 使得 Dist(B)+Dist(
我想通过计算每对中所有(多维)点集之间距离的平均值来量化组相似性。 我可以很容易地手动为每对组手动完成此操作,如下所示: library(dplyr) library(tibble) library(
在 OpenXML 中用于指定大小或 X、Y 坐标的度量单位是什么? (介绍)。 将那些与像素匹配是否有意义,如果是这样,那些如何转换为像素? graphicFrame.Transform = new
我想知道是否有人可以帮助我替换过渡层中的值。 如果我尝试: transitionlayer[transitionlayer >= 0.14] = 0.14 : comparison (5) is
我在 firebase 中有一个列表,其中包括地理位置(经度和纬度),并且我想获得距给定坐标最近的 10 个位置。 我正在从 MySQL 过渡,在那里我将计算 SELECT 中的距离, 并在 ORDE
如何在 Python 中根据 2 个 GPS 坐标计算速度、距离和方向(度)?每个点都有纬度、经度和时间。 我在这篇文章中找到了半正矢距离计算: Calculate distance between
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 6 年前。 Improve this ques
我只想使用 matplotlib 标记两条曲线之间发生最大偏差的位置。请帮助我。 垂直距离适用于 Kolmogorov–Smirnov test import numpy as np %matplot
我有一个包含数万行重复项的文件。我想根据行号找到重复项之间的平均时间/距离。 例如:(其中第一列是行号) 1 string1 2 string2 3 string2 4 string1 5 strin
用公式speed=distance/time计算时间 但时间总是0我的输入是 distance=10 和 speed=5 我的输出必须 = 2 #include int main() { in
我正在使用 Levenshtein 算法来查找两个字符串之间的相似性。这是我正在制作的程序的一个非常重要的部分,因此它需要有效。问题是该算法没有发现以下示例相似: CONAIR AIRCON 算法给出
对于一个房地产网站,我需要实现一个允许搜索文本和距离的搜索机制。 当 lat 和 lon 记录在单独的列中时,在 MySQL 表上进行距离计算很容易,但房子往往有 LOT true/false 属性。
是否可以在触发前更改 UIPanGestureRecognizer 的距离?目前的实现似乎在触发前有 5-10 像素的距离余量,我想降低它如果可能的话。 原因是我将 UIPanGestureRecog
我试图找到两个网格之间的偏差。例如在 3d 空间中定义的两组点之间的差异,我计划使用一些 3d 可视化工具来可视化距离,例如QT3d 或一些基于开放式 gl 的库。 我有两组网格,基本上是两个 .ST
所以,我有这个函数可以快速返回两个字符串之间的 Levenshtein 距离: Function Levenshtein(ByVal string1 As String, ByVal string2
我正在尝试用字典创建一个光学字符识别系统。 事实上,我还没有实现字典=) 我听说有一些基于 Levenstein 距离的简单指标,这些指标考虑了不同符号之间的不同距离。例如。 'N' 和 'H' 彼此
我在PostGIS数据库(-4326)中使用经纬度/经度SRID。我想以一种有效的方式找到最接近给定点的点。我试图做一个 ORDER BY ST_Distance(point, ST_GeomF
我想从线串的一端开始提取沿线串已知距离处的点的坐标。 例如: library(sf) path % group_by(L1) %>% summarise(do_union =
我已经编写了这些用于聚类基于序列的数据的函数: library(TraMineR) library(cluster) clustering <- function(data){ data <- s
是否可以设置 UILabel 的行之间的距离,因为我有一个 UILabel 包含 3 行,并且换行模式是自动换行? 最佳答案 如果您指的是“前导”,它指的是类型行之间的间隙 - 您无法在 UILabe
我是一名优秀的程序员,十分优秀!