- 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/
在过去的几个月里,我一直在研究 Haskell,我遇到了一个我不太确定如何处理的单子(monad)的情况。 我有一个 a -> m a 类型的值第二个类型为 m (a -> a)我需要对它们进行组合,
仿函数有 (a -> b) -> m a -> m b 应用程序有 f (a -> b) -> f a -> f b Monad 有 m a -> (a -> m b) -> m b 但是,是否有扩展
我是 Haskell 的新手,我想知道是否有比 Hoogle 更好的方法来确定一个库功能是否重复? 举个例子:我有很多函数f :: Monad a => a -> m a我想链接在一起,比如 f123
将存储在一系列列表中的 m、m、n 维数组组合成一个 m、m、n 维数组的方法是什么? 示例: 这是三个包含 m,m,n 维数组的列表: list1 <- array (1, dim = c(5, 5
有没有办法写一个函数f::(a -> b -> ... -> t) -> (Monad m => m a -> m b -> ... -> m t ),基本上是 liftMn 对于任何 n? (编辑:
我有一个像这样的 pandas 数据框: df = pd.DataFrame({'A':[1,3,2,9],'B':[2,1,2,7],'C':[7,2,4,6],'D':[8,1,6,4]},ind
这个问题来自文章“Trivial Monad”,地址:http://blog.sigfpe.com/2007/04/trivial-monad.html 。提供的答案是 h x y = x >>= (
所以>>= :: m a -> (a -> m b) -> m b和>> :: m a -> m b -> m b . 而 f b -> f a . 但我想要一些能m a -> (a -> m b)
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎与 help center 中定义的范围内的编程无关。 . 关闭 3 年前。 Improve
当我安装 rakudo来源: $ git clone git@github.com:rakudo/rakudo.git $ cd rakudo $ perl Configure.pl --gen-mo
我正在尝试通过查看一些练习来提高我的 Idris 技能 Software Foundations (最初是为 Coq 设计的,但我希望对 Idris 的翻译不会太糟糕)。我在使用 "Exercise:
我想知道以下是否可行。 与服务器交换密码时,应保护密码。因此,用户可以使用生成的 key kUser 来加密密码。 Encrypt(m, kUser) 生成加密消息 eU(m)。现在用户将此信息发送到
这两个表之间存在什么样的关系(1:1、1:m、m:m,等等)? CREATE TABLE IF NOT EXISTS `my_product` ( `id` int(11) NOT NULL au
有人可以解释类型的含义以及如何实现吗? class Foldable f where foldMap :: (Monoid m) => (a -> m) -> f a -> m 基于 https:
例如,在 MVC 应用程序中,我可以使用 Html 助手来创建这样的标签: @Html.LabelFor(m => m.ProductName) 我没有在任何地方声明变量“m”,但 IDE 会自动找出
更新:澄清、更明确的重点和缩短的示例: 我可以避免 M op+(M&&,M&&) 过载吗?假设,我想很好地处理 RValues?我想其他三个重载是必需的。 我首先使用 (&&,&&) 重载的原因: 通
假设我有一个函数,它接受两个向量并返回一个整数,例如一个向量中也存在另一个向量中的元素数量。喜欢: f m [,1] [,2] [,3] [1,] "c" "i" "c" [2,] "
我想将字符串(字幕)转换为: 585 00:59:59,237 --> 01:00:01,105 - It's all right. - He saw us! 586 01:00:01,139 -->
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求提供代码的问题必须表现出对所解决问题的最低限度理解。包括尝试过的解决方案、为什么它们不起作用,以及预
是否可以将 Linux 中的大文件将 d.m.Y h:m:s 转换为 Y-d-m h:m:s? 示例数据 "30.07.2016 00:00:00",DN123,PAPN,PAPN,TEST,9189
我是一名优秀的程序员,十分优秀!