- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个将8位图像缩小两倍的功能。我以前有optimised the rgb32 case with SSE。现在,我想对gray8案例做同样的事情。
在核心处,有一个函数接受两行像素数据,其工作方式如下:
/**
* Calculates the average of two rows of gray8 pixels by averaging four pixels.
*/
void average2Rows(const uint8_t* row1, const uint8_t* row2, uint8_t* dst, int size)
{
for (int i = 0; i < size - 1; i += 2)
*(dst++) = ((row1[i]+row1[i+1]+row2[i]+row2[i+1])/4)&0xFF;
}
/* row1: 16 8-bit values A-P
* row2: 16 8-bit values a-p
* returns 16 8-bit values (A+B+a+b)/4, (C+D+c+d)/4, ..., (O+P+o+p)/4
*/
__m128i avg16Bytes(const __m128i& row1, const __m128i& row2)
{
static const __m128i zero = _mm_setzero_si128();
__m128i ABCDEFGHIJKLMNOP = _mm_avg_epu8(row1_u8, row2);
__m128i ABCDEFGH = _mm_unpacklo_epi8(ABCDEFGHIJKLMNOP, zero);
__m128i IJKLMNOP = _mm_unpackhi_epi8(ABCDEFGHIJKLMNOP, zero);
__m128i AIBJCKDL = _mm_unpacklo_epi16( ABCDEFGH, IJKLMNOP );
__m128i EMFNGOHP = _mm_unpackhi_epi16( ABCDEFGH, IJKLMNOP );
__m128i AEIMBFJN = _mm_unpacklo_epi16( AIBJCKDL, EMFNGOHP );
__m128i CGKODHLP = _mm_unpackhi_epi16( AIBJCKDL, EMFNGOHP );
__m128i ACEGIKMO = _mm_unpacklo_epi16( AEIMBFJN, CGKODHLP );
__m128i BDFHJLNP = _mm_unpackhi_epi16( AEIMBFJN, CGKODHLP );
return _mm_avg_epu8(ACEGIKMO, BDFHJLNP);
}
/*
* Calculates the average of two rows of gray8 pixels by averaging four pixels.
*/
void average2Rows(const uint8_t* src1, const uint8_t* src2, uint8_t* dst, int size)
{
for(int i = 0;i<size-31; i+=32)
{
__m128i tl = _mm_loadu_si128((__m128i const*)(src1+i));
__m128i tr = _mm_loadu_si128((__m128i const*)(src1+i+16));
__m128i bl = _mm_loadu_si128((__m128i const*)(src2+i));
__m128i br = _mm_loadu_si128((__m128i const*)(src2+i+16)))
__m128i l_avg = avg16Bytes(tl, bl);
__m128i r_avg = avg16Bytes(tr, br);
_mm_storeu_si128((__m128i *)(dst+(i/2)), _mm_packus_epi16(l_avg, r_avg));
}
}
__m128i avg16Bytes(const __m128i& row1, const __m128i& row2)
{
// Average the first 16 values of src1 and src2:
__m128i avg = _mm_avg_epu8(row1, row2);
// Unpack and horizontal add:
avg = _mm_maddubs_epi16(avg, _mm_set1_epi8(1));
// Divide by 2:
return _mm_srli_epi16(avg, 1);
}
(a+b)/2 + (c+d)/2
而不是
(a+b+c+d)/4
,它可以作为我的原始实现,因此它具有相同的一乘四舍五入误差。
__m128i avg16Bytes(const __m128i& row1, const __m128i& row2)
{
// Unpack and horizontal add:
__m128i row1 = _mm_maddubs_epi16(row1_u8, _mm_set1_epi8(1));
__m128i row2 = _mm_maddubs_epi16(row2_u8, _mm_set1_epi8(1));
// vertical add:
__m128i avg = _mm_add_epi16(row1_avg, row2_avg);
// divide by 4:
return _mm_srli_epi16(avg, 2);
}
最佳答案
如果您愿意接受两次使用pavgb
的双舍入处理,则可以通过首先使用pavgb
进行垂直平均来比Paul R的答案更快,将需要解压缩为16位元素的数据量减少了一半。 (并将一半的负载折叠到pavgb
的内存操作数中,从而减少了某些CPU的前端瓶颈。)
对于水平平均,最好的选择可能仍然是pmaddubsw
和set1(1)
并移位1,然后打包。
// SSSE3 version
// I used `__restrict__` to give the compiler more flexibility in unrolling
void average2Rows_doubleround(const uint8_t* __restrict__ src1, const uint8_t*__restrict__ src2,
uint8_t*__restrict__ dst, size_t size)
{
const __m128i vk1 = _mm_set1_epi8(1);
size_t dstsize = size/2;
for (size_t i = 0; i < dstsize - 15; i += 16)
{
__m128i v0 = _mm_load_si128((const __m128i *)&src1[i*2]);
__m128i v1 = _mm_load_si128((const __m128i *)&src1[i*2 + 16]);
__m128i v2 = _mm_load_si128((const __m128i *)&src2[i*2]);
__m128i v3 = _mm_load_si128((const __m128i *)&src2[i*2 + 16]);
__m128i left = _mm_avg_epu8(v0, v2);
__m128i right = _mm_avg_epu8(v1, v3);
__m128i w0 = _mm_maddubs_epi16(left, vk1); // unpack and horizontal add
__m128i w1 = _mm_maddubs_epi16(right, vk1);
w0 = _mm_srli_epi16(w0, 1); // divide by 2
w1 = _mm_srli_epi16(w1, 1);
w0 = _mm_packus_epi16(w0, w1); // pack
_mm_storeu_si128((__m128i *)&dst[i], w0);
}
}
_mm_srli_epi16(v, 8)
,用于将每个水平对中的奇数元素与偶数元素对齐。但是,由于没有水平截断的水平包装,因此在包装之前,您必须先对两半进行
_mm_and_si128(v, _mm_set1_epi16(0x00FF))
编码。事实证明,这比使用SSSE3
pmaddubsw
慢,尤其是在没有AVX的情况下,AVX需要额外的MOVDQA指令来复制寄存器。
void average2Rows_doubleround_SSE2(const uint8_t* __restrict__ src1, const uint8_t* __restrict__ src2, uint8_t* __restrict__ dst, size_t size)
{
size /= 2;
for (size_t i = 0; i < size - 15; i += 16)
{
__m128i v0 = _mm_load_si128((__m128i *)&src1[i*2]);
__m128i v1 = _mm_load_si128((__m128i *)&src1[i*2 + 16]);
__m128i v2 = _mm_load_si128((__m128i *)&src2[i*2]);
__m128i v3 = _mm_load_si128((__m128i *)&src2[i*2 + 16]);
__m128i left = _mm_avg_epu8(v0, v2);
__m128i right = _mm_avg_epu8(v1, v3);
__m128i l_odd = _mm_srli_epi16(left, 8); // line up horizontal pairs
__m128i r_odd = _mm_srli_epi16(right, 8);
__m128i l_avg = _mm_avg_epu8(left, l_odd); // leaves garbage in the high halves
__m128i r_avg = _mm_avg_epu8(right, r_odd);
l_avg = _mm_and_si128(l_avg, _mm_set1_epi16(0x00FF));
r_avg = _mm_and_si128(r_avg, _mm_set1_epi16(0x00FF));
__m128i avg = _mm_packus_epi16(l_avg, r_avg); // pack
_mm_storeu_si128((__m128i *)&dst[i], avg);
}
}
_mm_cvtepi16_epi8
,但是IACA表示它在Skylake-AVX512上是2 oups,并且只需要输入1并产生半角输出。根据IACA,内存目标形式是4个未融合域的总和(与reg,reg +单独存储相同)。我必须使用
_mm_mask_cvtepi16_storeu_epi8(&dst\[i+0\], -1, l_avg);
来获取它,因为gcc和clang无法将单独的
_mm_store
折叠到
vpmovwb
的存储目标中。 (没有非屏蔽存储内在函数,因为编译器应该像为典型ALU指令将
_mm_load
折叠到内存操作数中一样为您做到这一点)。
cvtepi64_epi8
)而不是一半时,它才有用。也许对避免再次洗牌以处理
_mm512_packus_epi16
的车内行为很有用。使用AVX2,在
_mm256_packus_epi16
上的
[D C] [B A]
之后,您拥有
[D B | C A]
,可以使用AVX2
_mm256_permute4x64_epi64 (__m256i a, const int imm8)
对其进行修复,以按64位块进行随机播放。但是对于AVX512,您需要对
vpermq
进行 vector 随机播放控制。
packus
+修正改组可能仍然是一个更好的选择。
i/2
进行算术右移,而不是增加16(而不是32)并使用缩放索引寻址模式进行加载。看来他们没有意识到
i
总是偶数。
.LBB1_2: ## clang's inner loop for int i, dst[i/2] version
movdqu xmm1, xmmword ptr [rdi + rcx]
movdqu xmm2, xmmword ptr [rdi + rcx + 16]
movdqu xmm3, xmmword ptr [rsi + rcx]
movdqu xmm4, xmmword ptr [rsi + rcx + 16]
pavgb xmm3, xmm1
pavgb xmm4, xmm2
pmaddubsw xmm3, xmm0
pmaddubsw xmm4, xmm0
psrlw xmm3, 1
psrlw xmm4, 1
packuswb xmm3, xmm4
mov eax, ecx # This whole block is wasted instructions!!!
shr eax, 31
add eax, ecx
sar eax # eax = ecx/2, with correct rounding even for negative `i`
cdqe # sign-extend EAX into RAX
movdqu xmmword ptr [rdx + rax], xmm3
add rcx, 32 # i += 32
cmp rcx, r8
jl .LBB1_2 # }while(i < size-31)
mov
/
sar
/
movsx
),但是gcc5.x和6.x分别为src1和src2以及商店的计数器/索引单独增加了指针。 (完全脑残行为,尤其是因为他们仍然使用
-march=sandybridge
这样做。索引的
movdqu
存储和未索引的
movdqu
加载为您提供了最大的循环开销。)
dstsize
并乘以
i
而不是将其除以给出更好的结果。 gcc和clang的不同版本将其可靠地编译为单个循环计数器,它们与缩放索引寻址模式一起用于负载。您得到的代码如下:
movdqa xmm1, xmmword ptr [rdi + 2*rax]
movdqa xmm2, xmmword ptr [rdi + 2*rax + 16]
pavgb xmm1, xmmword ptr [rsi + 2*rax]
pavgb xmm2, xmmword ptr [rsi + 2*rax + 16] # saving instructions with aligned loads, see below
...
movdqu xmmword ptr [rdx + rax], xmm1
add rax, 16
cmp rax, rcx
jb .LBB0_2
size_t i
来匹配
size_t
的大小,以确保gcc不会浪费任何指令将其符号扩展或零扩展到指针的宽度。 (不过,零扩展通常是免费的,因此
unsigned size
和
unsigned i
可能还行,并保存了几个REX前缀。)
cmp
,但将索引的计数提高到0,这比我所做的要快得多。我不确定让编译器变得不愚蠢并且如果您算上零就忽略
cmp
指令会多么容易。不过,从对象末尾开始索引是没有问题的。
src1+=size;
。但是,如果您要使用unaligned-cleanup循环,它会使事情复杂化。
Performance counter stats for './grayscale-dowscale-by-2.inline.gcc-skylake-noavx' (2 runs):
2731.607950 task-clock (msec) # 1.000 CPUs utilized ( +- 0.40% )
2 context-switches # 0.001 K/sec ( +- 20.00% )
0 cpu-migrations # 0.000 K/sec
88 page-faults:u # 0.032 K/sec ( +- 0.57% )
11,917,723,707 cycles # 4.363 GHz ( +- 0.07% )
42,006,654,015 instructions # 3.52 insn per cycle ( +- 0.00% )
41,908,837,143 uops_issued_any # 15342.186 M/sec ( +- 0.00% )
49,409,631,052 uops_executed_thread # 18088.112 M/sec ( +- 0.00% )
3,301,193,901 branches # 1208.517 M/sec ( +- 0.00% )
100,013,629 branch-misses # 3.03% of all branches ( +- 0.01% )
2.731715466 seconds time elapsed ( +- 0.40% )
int i
和dst[i/2]
一起创建了更高的循环开销(更多标量指令):
Performance counter stats for './grayscale-dowscale-by-2.loopoverhead-aligned-inline.gcc-skylake-noavx' (2 runs):
3314.335833 task-clock (msec) # 1.000 CPUs utilized ( +- 0.02% )
4 context-switches # 0.001 K/sec ( +- 14.29% )
0 cpu-migrations # 0.000 K/sec
88 page-faults:u # 0.026 K/sec ( +- 0.57% )
14,531,925,552 cycles # 4.385 GHz ( +- 0.06% )
51,607,478,414 instructions # 3.55 insn per cycle ( +- 0.00% )
51,109,303,460 uops_issued_any # 15420.677 M/sec ( +- 0.00% )
55,810,234,508 uops_executed_thread # 16839.040 M/sec ( +- 0.00% )
3,301,344,602 branches # 996.080 M/sec ( +- 0.00% )
100,025,451 branch-misses # 3.03% of all branches ( +- 0.00% )
3.314418952 seconds time elapsed ( +- 0.02% )
Performance counter stats for './grayscale-dowscale-by-2.paulr-inline.gcc-skylake-noavx' (2 runs):
3751.990587 task-clock (msec) # 1.000 CPUs utilized ( +- 0.03% )
3 context-switches # 0.001 K/sec
0 cpu-migrations # 0.000 K/sec
88 page-faults:u # 0.024 K/sec ( +- 0.56% )
16,323,525,446 cycles # 4.351 GHz ( +- 0.04% )
58,008,101,634 instructions # 3.55 insn per cycle ( +- 0.00% )
57,610,721,806 uops_issued_any # 15354.709 M/sec ( +- 0.00% )
55,505,321,456 uops_executed_thread # 14793.566 M/sec ( +- 0.00% )
3,301,456,435 branches # 879.921 M/sec ( +- 0.00% )
100,001,954 branch-misses # 3.03% of all branches ( +- 0.02% )
3.752086635 seconds time elapsed ( +- 0.03% )
Performance counter stats for './grayscale-dowscale-by-2.loopoverhead-paulr-inline.gcc-skylake-noavx' (2 runs):
4154.300887 task-clock (msec) # 1.000 CPUs utilized ( +- 0.01% )
3 context-switches # 0.001 K/sec
0 cpu-migrations # 0.000 K/sec
90 page-faults:u # 0.022 K/sec ( +- 1.68% )
18,174,791,383 cycles # 4.375 GHz ( +- 0.03% )
67,608,724,157 instructions # 3.72 insn per cycle ( +- 0.00% )
66,937,292,129 uops_issued_any # 16112.769 M/sec ( +- 0.00% )
61,875,610,759 uops_executed_thread # 14894.350 M/sec ( +- 0.00% )
3,301,571,922 branches # 794.736 M/sec ( +- 0.00% )
100,029,270 branch-misses # 3.03% of all branches ( +- 0.00% )
4.154441330 seconds time elapsed ( +- 0.01% )
for()
循环,因此L1D缓存中的所有内容都很热门。内部循环的32次迭代在这里接近最坏的情况:对于频繁的错误预测而言足够低,但又不那么低,以至于分支预测可以选择并避免这种模式。
dst[i/2]
函数的非内联版本(具有未知的
size
),但未展开下部循环-开销版本。
clang++-4.0 -O3 -march=skylake -mno-avx
,我的版本(编译器将其展开为2)运行于:9.61G周期,适用于100M iters(2.2s)。 (发出了35.6G的uops(融合域),执行了45.0G的uops(融合域),接近零的分支未命中。)可能不再是前端的瓶颈,但是AVX仍然会受到伤害。
movdqa
指令来复制寄存器)。即使没有AVX2来实现更宽的整数 vector ,AVX也将有助于无损 vector 指令。
pavgb
与内存操作数一起使用,而不是使用单独的未对齐加载指令。这意味着更少的指令和更少的前端操作,这是此循环的瓶颈。
pmaddubsw
的第二个操作数只能来自内存,这就是被视为有符号字节的那个。如果我们使用
_mm_maddubs_epi16(_mm_set1_epi8(1), v0);
,则16位乘法结果将被符号扩展而不是零扩展。因此
1+255
会显示为0,而不是256。
vpavgb xmm0, xmm0, [rsi+rax*2]
在发布到内核的乱序部分之前,在Haswell / Skylake上未分层为2 oups,但是
pavgb xmm1, [rsi+rax*2]
可以一直保持微融合状态,因此它作为单个uop发出。前端问题瓶颈是除Ryzen(即非Atom / Silvermont)以外的主流x86 CPU上每个时钟4个融合域uops。将一半的负载折叠到内存操作数中,可以帮助除Sandybridge / Ivybridge之外的所有Intel CPU和所有AMD CPU减轻负载。
alignas(32)
的测试函数中时,即使您使用
_mm_loadu
内部函数,gcc和clang也会折叠负载。他们知道数据是一致的,并且可以利用。
-march=native
)的情况下编译128b向量化的代码实际上会减慢Haswell / Skylake的速度,因为即使这四个负载都是
vpavgb
的内存操作数,它也会使所有4个负载以单独的uops发出没有AVX会避免的任何
movdqa
寄存器复制指令。 (由于3操作数指令不会破坏其输入之一,因此,即使对于仍仅使用128b vector 的手动矢量化代码,AVX通常还是会领先于此。)在这种情况下,
13,53G cycles ( +- 0.05% )
或
3094.195773 ms ( +- 0.20% )
是从
11.92G
循环开始的约2.7秒。 uops_issued =
48.508G
,高于
41,908
。指令计数和uops_exected计数基本相同。
pavgb zmm
has 1 per clock throughput while pavgb ymm
is 2 per clock.的原因。)
main()
中,我只是在每个数组的前面添加了
alignas(32)
。
-march=native
,因此可以使用
#ifdef __AVX2__
在编译时轻松检测AVX2。没有简单的方法对128b和256b手动矢量化使用完全相同的代码。所有内部函数都有不同的名称,因此即使没有其他差异,您通常也需要复制所有内容。
关于c++ - 使用SSE最快缩小8位灰度图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45541530/
我在网上搜索但没有找到任何合适的文章解释如何使用 javascript 使用 WCF 服务,尤其是 WebScriptEndpoint。 任何人都可以对此给出任何指导吗? 谢谢 最佳答案 这是一篇关于
我正在编写一个将运行 Linux 命令的 C 程序,例如: cat/etc/passwd | grep 列表 |剪切-c 1-5 我没有任何结果 *这里 parent 等待第一个 child (chi
所以我正在尝试处理文件上传,然后将该文件作为二进制文件存储到数据库中。在我存储它之后,我尝试在给定的 URL 上提供文件。我似乎找不到适合这里的方法。我需要使用数据库,因为我使用 Google 应用引
我正在尝试制作一个宏,将下面的公式添加到单元格中,然后将其拖到整个列中并在 H 列中复制相同的公式 我想在 F 和 H 列中输入公式的数据 Range("F1").formula = "=IF(ISE
问题类似于this one ,但我想使用 OperatorPrecedenceParser 解析带有函数应用程序的表达式在 FParsec . 这是我的 AST: type Expression =
我想通过使用 sequelize 和 node.js 将这个查询更改为代码取决于在哪里 select COUNT(gender) as genderCount from customers where
我正在使用GNU bash,版本5.0.3(1)-发行版(x86_64-pc-linux-gnu),我想知道为什么简单的赋值语句会出现语法错误: #/bin/bash var1=/tmp
这里,为什么我的代码在 IE 中不起作用。我的代码适用于所有浏览器。没有问题。但是当我在 IE 上运行我的项目时,它发现错误。 而且我的 jquery 类和 insertadjacentHTMl 也不
我正在尝试更改标签的innerHTML。我无权访问该表单,因此无法编辑 HTML。标签具有的唯一标识符是“for”属性。 这是输入和标签的结构:
我有一个页面,我可以在其中返回用户帖子,可以使用一些 jquery 代码对这些帖子进行即时评论,在发布新评论后,我在帖子下插入新评论以及删除 按钮。问题是 Delete 按钮在新插入的元素上不起作用,
我有一个大约有 20 列的“管道分隔”文件。我只想使用 sha1sum 散列第一列,它是一个数字,如帐号,并按原样返回其余列。 使用 awk 或 sed 执行此操作的最佳方法是什么? Accounti
我需要将以下内容插入到我的表中...我的用户表有五列 id、用户名、密码、名称、条目。 (我还没有提交任何东西到条目中,我稍后会使用 php 来做)但由于某种原因我不断收到这个错误:#1054 - U
所以我试图有一个输入字段,我可以在其中输入任何字符,但然后将输入的值小写,删除任何非字母数字字符,留下“。”而不是空格。 例如,如果我输入: 地球的 70% 是水,-!*#$^^ & 30% 土地 输
我正在尝试做一些我认为非常简单的事情,但出于某种原因我没有得到想要的结果?我是 javascript 的新手,但对 java 有经验,所以我相信我没有使用某种正确的规则。 这是一个获取输入值、检查选择
我想使用 angularjs 从 mysql 数据库加载数据。 这就是应用程序的工作原理;用户登录,他们的用户名存储在 cookie 中。该用户名显示在主页上 我想获取这个值并通过 angularjs
我正在使用 autoLayout,我想在 UITableViewCell 上放置一个 UIlabel,它应该始终位于单元格的右侧和右侧的中心。 这就是我想要实现的目标 所以在这里你可以看到我正在谈论的
我需要与 MySql 等效的 elasticsearch 查询。我的 sql 查询: SELECT DISTINCT t.product_id AS id FROM tbl_sup_price t
我正在实现代码以使用 JSON。 func setup() { if let flickrURL = NSURL(string: "https://api.flickr.com/
我尝试使用for循环声明变量,然后测试cols和rols是否相同。如果是,它将运行递归函数。但是,我在 javascript 中执行 do 时遇到问题。有人可以帮忙吗? 现在,在比较 col.1 和
我举了一个我正在处理的问题的简短示例。 HTML代码: 1 2 3 CSS 代码: .BB a:hover{ color: #000; } .BB > li:after {
我是一名优秀的程序员,十分优秀!