- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
SVML的__m256d _mm256_log2_pd (__m256d a)
在除Intel以外的其他编译器上不可用,他们说它的性能在AMD处理器上受到限制。互联网上有一些AVX log intrinsics (_mm256_log_ps) missing in g++-4.8?和SIMD math libraries for SSE and AVX引用的实现,但是它们似乎比AVX2更具SSE。还有Agner Fog's vector library,但是它是一个很大的库,其中包含的内容远远超过了vector log2,因此从实现中很难仅找出vector log2操作的基本部分。
因此,有人可以解释一下如何有效地对4个log2()
数字的 vector 实现double
操作吗?即类似于__m256d _mm256_log2_pd (__m256d a)
的功能,但可用于其他编译器,并且对于AMD和Intel处理器都相当有效。
编辑:在我当前的特定情况下,数字是0到1之间的概率,并且对数用于熵计算:对i
的所有P[i]*log(P[i])
求和的求和。 P[i]
的浮点指数范围很大,因此数字可以接近0。我不确定精度,因此会考虑以30个尾数开头的任何解决方案,尤其是可调整的解决方案是首选。
EDIT2:这是到目前为止,根据https://en.wikipedia.org/wiki/Logarithm#Power_series的“更有效的系列”,我的实现。如何改善? (性能和准确性都需要提高)
namespace {
const __m256i gDoubleExpMask = _mm256_set1_epi64x(0x7ffULL << 52);
const __m256i gDoubleExp0 = _mm256_set1_epi64x(1023ULL << 52);
const __m256i gTo32bitExp = _mm256_set_epi32(0, 0, 0, 0, 6, 4, 2, 0);
const __m128i gExpNormalizer = _mm_set1_epi32(1023);
//TODO: some 128-bit variable or two 64-bit variables here?
const __m256d gCommMul = _mm256_set1_pd(2.0 / 0.693147180559945309417); // 2.0/ln(2)
const __m256d gCoeff1 = _mm256_set1_pd(1.0 / 3);
const __m256d gCoeff2 = _mm256_set1_pd(1.0 / 5);
const __m256d gCoeff3 = _mm256_set1_pd(1.0 / 7);
const __m256d gCoeff4 = _mm256_set1_pd(1.0 / 9);
const __m256d gVect1 = _mm256_set1_pd(1.0);
}
__m256d __vectorcall Log2(__m256d x) {
const __m256i exps64 = _mm256_srli_epi64(_mm256_and_si256(gDoubleExpMask, _mm256_castpd_si256(x)), 52);
const __m256i exps32_avx = _mm256_permutevar8x32_epi32(exps64, gTo32bitExp);
const __m128i exps32_sse = _mm256_castsi256_si128(exps32_avx);
const __m128i normExps = _mm_sub_epi32(exps32_sse, gExpNormalizer);
const __m256d expsPD = _mm256_cvtepi32_pd(normExps);
const __m256d y = _mm256_or_pd(_mm256_castsi256_pd(gDoubleExp0),
_mm256_andnot_pd(_mm256_castsi256_pd(gDoubleExpMask), x));
// Calculate t=(y-1)/(y+1) and t**2
const __m256d tNum = _mm256_sub_pd(y, gVect1);
const __m256d tDen = _mm256_add_pd(y, gVect1);
const __m256d t = _mm256_div_pd(tNum, tDen);
const __m256d t2 = _mm256_mul_pd(t, t); // t**2
const __m256d t3 = _mm256_mul_pd(t, t2); // t**3
const __m256d terms01 = _mm256_fmadd_pd(gCoeff1, t3, t);
const __m256d t5 = _mm256_mul_pd(t3, t2); // t**5
const __m256d terms012 = _mm256_fmadd_pd(gCoeff2, t5, terms01);
const __m256d t7 = _mm256_mul_pd(t5, t2); // t**7
const __m256d terms0123 = _mm256_fmadd_pd(gCoeff3, t7, terms012);
const __m256d t9 = _mm256_mul_pd(t7, t2); // t**9
const __m256d terms01234 = _mm256_fmadd_pd(gCoeff4, t9, terms0123);
const __m256d log2_y = _mm256_mul_pd(terms01234, gCommMul);
const __m256d log2_x = _mm256_add_pd(log2_y, expsPD);
return log2_x;
}
#include <chrono>
#include <cmath>
#include <cstdio>
#include <immintrin.h>
// ... Log2() implementation here
const int64_t cnLogs = 100 * 1000 * 1000;
void BenchmarkLog2Vect() {
__m256d sums = _mm256_setzero_pd();
auto start = std::chrono::high_resolution_clock::now();
for (int64_t i = 1; i <= cnLogs; i += 4) {
const __m256d x = _mm256_set_pd(double(i+3), double(i+2), double(i+1), double(i));
const __m256d logs = Log2(x);
sums = _mm256_add_pd(sums, logs);
}
auto elapsed = std::chrono::high_resolution_clock::now() - start;
double nSec = 1e-6 * std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count();
double sum = sums.m256d_f64[0] + sums.m256d_f64[1] + sums.m256d_f64[2] + sums.m256d_f64[3];
printf("Vect Log2: %.3lf Ops/sec calculated %.3lf\n", cnLogs / nSec, sum);
}
std::log2()
快4倍,比
std::log()
快2.5倍。
最佳答案
通常的策略基于身份log(a*b) = log(a) + log(b)
或本例中的log2( 2^exponent * mantissa) ) = log2( 2^exponent ) + log2(mantissa)
。或简化为exponent + log2(mantissa)
。尾数的范围非常有限,在1.0到2.0之间,因此log2(mantissa)
的多项式只能在该非常有限的范围内进行拟合。 (或等效地,尾数= 0.5到1.0,并将指数偏差校正常数更改为1)。
泰勒级数展开是系数的一个很好的起点,但是您通常希望最小化该特定范围内的最大绝对误差(或相对误差),并且泰勒级数系数可能在该范围内具有较低或较高的离群值,而不是使最大正误差与最大负误差几乎匹配。因此,您可以进行系数的极大极小拟合。
如果您的函数将log2(1.0)
评估为精确地为0.0
很重要,则可以安排将mantissa-1.0
实际用作多项式,而无需使用恒定系数,以安排发生这种情况。 0.0 ^ n = 0.0
。即使绝对误差仍然很小,这也极大地改善了接近1.0的输入的相对误差。
您需要它有多精确,以及在什么输入范围内?像往常一样,在精度和速度之间需要权衡取舍,但幸运的是,通过例如再增加一个多项式项(并重新拟合系数),或通过舍弃一些舍入误差来避免。
Agner Fog's VCL implementation of log_d()
的目标是达到极高的准确性,它使用技巧来避免舍入错误,从而避免了可能会导致加或减的数量增加的事情。这使基本设计有些模糊。
有关更快更近似的float
log()
的信息,请参见http://jrfonseca.blogspot.ca/2008/09/fast-sse2-pow-tables-or-polynomials.html的多项式实现。它遗漏了VCL使用的许多额外的精确增益技巧,因此更容易理解。它使用1.0到2.0范围内的尾数的多项式近似值。
(这是log()
实现的真正诀窍:您只需要一个可在较小范围内使用的多项式。)
它已经只执行log2
而不是log
了,这与VCL中将log-base-e引入常量及其使用方式不同。阅读它可能是理解exponent + polynomial(mantissa)
的log()
实现的一个很好的起点。
即使是最高精度的版本也不是完整的float
精度,更不用说double
了,但是您可以使用更多项来拟合多项式。显然,两个多项式之比效果很好;这就是VCL用于double
的方式。
将JRF的SSE2函数移植到AVX2 + FMA(尤其是带有_mm512_getexp_ps
和_mm512_getmant_ps
的AVX512)后,我进行了仔细的调整,就获得了出色的结果。 (这是一个商业项目的一部分,所以我认为我不能发布代码。)float
的快速近似实现正是我想要的。
在我的用例中,每个jrf_fastlog()
是独立的,因此OOO执行很好地隐藏了FMA延迟,甚至不值得使用VCL's polynomial_5()
function使用的更高ILP较短延迟多项式评估方法("Estrin's scheme",它执行一些非FMA在FMA之前相乘,从而产生更多的总指令)。
Agner Fog的VCL现在已获得Apache许可,因此任何项目都可以直接包含它。如果需要高精度,则应直接使用VCL。它仅是标题,只是内联函数,因此不会膨胀您的二进制文件。
VCL的log
float和double函数位于 vectormath_exp.h
中。该算法有两个主要部分:
double
范围内[0.5, 1.0)
值的 vector 。 (或者(0.5, 1.0]
,我忘记了)。if(mantissa <= SQRT2*0.5) { mantissa += mantissa; exponent++;}
和mantissa -= 1.0
进行调整。log(x)
的多项式近似值,其精确度约为x = 1.0。 (对于double
,VCL的log_d()
使用两个5阶多项式的比率。@harold says this is often good for precision。一个除法器与许多FMA混合通常不会影响吞吐量,但确实比FMA具有更高的延迟。使用vrcpps
+ Newton- Raphson迭代通常比仅在现代硬件上使用vdivps
慢,使用比率还可以通过并行评估两个低阶多项式(而不是一个高阶多项式)来创建更多的ILP,并且与一个较长的dep链相比,可以降低总延迟。一个高阶多项式(也会沿该长链累积明显的舍入误差)。exponent + polynomial_approx_log(mantissa)
以获取最终的log()结果。 VCL分多个步骤执行此操作以减少舍入错误。 ln2_lo + ln2_hi = ln(2)
。它分为一个大常数和一个小常数,以减少舍入误差。// res is the polynomial(adjusted_mantissa) result
// fe is the float exponent
// x is the adjusted_mantissa. x2 = x*x;
res = mul_add(fe, ln2_lo, res); // res += fe * ln2_lo;
res += nmul_add(x2, 0.5, x); // res += x - 0.5 * x2;
res = mul_add(fe, ln2_hi, res); // res += fe * ln2_hi;
ln2
的东西,仅使用VM_LN2
。x - 0.5*x2
部分确实是一个额外的多项式术语。这就是我引入对数基数e的意思:您需要在这些条件上使用一个系数,或者要摆脱那条线并重新拟合log2的多项式系数。您不能只将所有多项式系数乘以一个常数。float
类型化为uint32_t
,然后将该整数转换为float
。由于IEEE binary32 float将指数存储在比尾数更高的位中,因此生成的float
主要表示指数的值,由1 << 23
缩放,但也包含尾数信息。log()
近似值。它包括对(constant + mantissa)
的除法,以在将float位模式转换为float
时校正尾数污染。我发现,与带有4阶多项式的JRF faSTLog相比,在HSW和SKL上使用AVX2进行矢量化处理的速度较慢且准确性较低。 (尤其是将其用作快速arcsinh
的一部分时,它也对vsqrtps
使用了除法单元。)关于c++ - 在AVX2中有效实现log2(__ m256d),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45770089/
背景: 我最近一直在使用 JPA,我为相当大的关系数据库项目生成持久层的轻松程度给我留下了深刻的印象。 我们公司使用大量非 SQL 数据库,特别是面向列的数据库。我对可能对这些数据库使用 JPA 有一
我已经在我的 maven pom 中添加了这些构建配置,因为我希望将 Apache Solr 依赖项与 Jar 捆绑在一起。否则我得到了 SolarServerException: ClassNotF
interface ITurtle { void Fight(); void EatPizza(); } interface ILeonardo : ITurtle {
我希望可用于 Java 的对象/关系映射 (ORM) 工具之一能够满足这些要求: 使用 JPA 或 native SQL 查询获取大量行并将其作为实体对象返回。 允许在行(实体)中进行迭代,并在对当前
好像没有,因为我有实现From for 的代码, 我可以转换 A到 B与 .into() , 但同样的事情不适用于 Vec .into()一个Vec . 要么我搞砸了阻止实现派生的事情,要么这不应该发
在 C# 中,如果 A 实现 IX 并且 B 继承自 A ,是否必然遵循 B 实现 IX?如果是,是因为 LSP 吗?之间有什么区别吗: 1. Interface IX; Class A : IX;
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我正在阅读标准haskell库的(^)的实现代码: (^) :: (Num a, Integral b) => a -> b -> a x0 ^ y0 | y0 a -> b ->a expo x0
我将把国际象棋游戏表示为 C++ 结构。我认为,最好的选择是树结构(因为在每个深度我们都有几个可能的移动)。 这是一个好的方法吗? struct TreeElement{ SomeMoveType
我正在为用户名数据库实现字符串匹配算法。我的方法采用现有的用户名数据库和用户想要的新用户名,然后检查用户名是否已被占用。如果采用该方法,则该方法应该返回带有数据库中未采用的数字的用户名。 例子: “贾
我正在尝试实现 Breadth-first search algorithm , 为了找到两个顶点之间的最短距离。我开发了一个 Queue 对象来保存和检索对象,并且我有一个二维数组来保存两个给定顶点
我目前正在 ika 中开发我的 Python 游戏,它使用 python 2.5 我决定为 AI 使用 A* 寻路。然而,我发现它对我的需要来说太慢了(3-4 个敌人可能会落后于游戏,但我想供应 4-
我正在寻找 Kademlia 的开源实现C/C++ 中的分布式哈希表。它必须是轻量级和跨平台的(win/linux/mac)。 它必须能够将信息发布到 DHT 并检索它。 最佳答案 OpenDHT是
我在一本书中读到这一行:-“当我们要求 C++ 实现运行程序时,它会通过调用此函数来实现。” 而且我想知道“C++ 实现”是什么意思或具体是什么。帮忙!? 最佳答案 “C++ 实现”是指编译器加上链接
我正在尝试使用分支定界的 C++ 实现这个背包问题。此网站上有一个 Java 版本:Implementing branch and bound for knapsack 我试图让我的 C++ 版本打印
在很多情况下,我需要在 C# 中访问合适的哈希算法,从重写 GetHashCode 到对数据执行快速比较/查找。 我发现 FNV 哈希是一种非常简单/好/快速的哈希算法。但是,我从未见过 C# 实现的
目录 LRU缓存替换策略 核心思想 不适用场景 算法基本实现 算法优化
1. 绪论 在前面文章中提到 空间直角坐标系相互转换 ,测绘坐标转换时,一般涉及到的情况是:两个直角坐标系的小角度转换。这个就是我们经常在测绘数据处理中,WGS-84坐标系、54北京坐标系
在软件开发过程中,有时候我们需要定时地检查数据库中的数据,并在发现新增数据时触发一个动作。为了实现这个需求,我们在 .Net 7 下进行一次简单的演示. PeriodicTimer .
二分查找 二分查找算法,说白了就是在有序的数组里面给予一个存在数组里面的值key,然后将其先和数组中间的比较,如果key大于中间值,进行下一次mid后面的比较,直到找到相等的,就可以得到它的位置。
我是一名优秀的程序员,十分优秀!