相关疑难解决方法(0)

用64位替换32位循环计数器会引入疯狂的性能偏差

我一直在寻找最快的方法来处理popcount大数据.我遇到了一个很奇怪的效果:改变从循环变量unsigneduint64_t50%在我的电脑上所做的性能下降.

基准

#include <iostream>
#include <chrono>
#include <x86intrin.h>

int main(int argc, char* argv[]) {

    using namespace std;
    if (argc != 2) {
       cerr << "usage: array_size in MB" << endl;
       return -1;
    }

    uint64_t size = atol(argv[1])<<20;
    uint64_t* buffer = new uint64_t[size/8];
    char* charbuffer = reinterpret_cast<char*>(buffer);
    for (unsigned i=0; i<size; ++i)
        charbuffer[i] = rand()%256;

    uint64_t count,duration;
    chrono::time_point<chrono::system_clock> startP,endP;
    {
        startP = chrono::system_clock::now();
        count = 0;
        for( unsigned k = 0; k < …
Run Code Online (Sandbox Code Playgroud)

c++ performance x86 assembly compiler-optimization

1370
推荐指数
9
解决办法
15万
查看次数

在C中以整数查找最高设置位(msb)的最快/最有效方法是什么?

如果我有一个整数n,并且我想知道最高位的位置(也就是说,如果最低有效位在右边,我想知道最左边位的位置是1),找出最快捷/最有效的方法是什么?

我知道POSIX支持ffs()strings.h中的一个方法来查找第一个设置位,但似乎没有相应的fls()方法.

是否有一些非常明显的方法可以解决这个问题?

如果你不能使用POSIX功能来实现可移植性呢?

编辑:如何在32位和64位架构上运行的解决方案(许多代码清单似乎只能在32位整数上运行).

c algorithm optimization bit-manipulation

112
推荐指数
11
解决办法
11万
查看次数

用于找到大于或等于给定值的2的最小幂的算法

我需要找到大于或等于给定值的2的最小幂.到目前为止,我有这个:

int value = 3221; // 3221 is just an example, could be any number
int result = 1;

while (result < value) result <<= 1;
Run Code Online (Sandbox Code Playgroud)

它工作正常,但感觉有点幼稚.这个问题有更好的算法吗?

编辑.有一些很好的Assembler建议,所以我将这些标签添加到问题中.

c++ algorithm assembly

48
推荐指数
7
解决办法
4万
查看次数

找到位数组中设置的最高有效位(最左侧)

我有一个位数组实现,其中第0个索引是数组中第一个字节的MSB,第8个索引是第二个字节的MSB,等等...

找到这个位数组中设置的第一个位的快速方法是什么?我查找的所有相关解决方案都找到了第一个最重要的位,但我需要第一个最重要的解决方案.所以,给定0x00A1,我想要8(因为它是左起第9位).

c 32-bit bit-manipulation

38
推荐指数
5
解决办法
7万
查看次数

为什么g ++将计算推入热循环?

我有一个非常奇怪的编译器行为,其中G ++将计算拉入热循环,严重降低了生成的代码的性能.这里发生了什么?

考虑这个功能:

#include <cstdint>

constexpr bool noLambda = true;

void funnyEval(const uint8_t* columnData, uint64_t dataOffset, uint64_t dictOffset, int32_t iter, int32_t limit, int32_t* writer,const int32_t* dictPtr2){
   // Computation X1
   const int32_t* dictPtr = reinterpret_cast<const int32_t*>(columnData + dictOffset);
   // Computation X2
   const uint16_t* data = (const uint16_t*)(columnData + dataOffset);
   // 1. The less broken solution without lambda
   if (noLambda) {
        for (;iter != limit;++iter){
            int32_t t=dictPtr[data[iter]];
            *writer = t;
            writer++;
        }
   }
   // 2. The totally broken solution with lambda
   else …
Run Code Online (Sandbox Code Playgroud)

c++ optimization assembly gcc compiler-optimization

34
推荐指数
2
解决办法
873
查看次数

max(ctz(x), ctz(y)) 是否有更快的算法?

对于min(ctz(x), ctz(y)),我们可以使用ctz(x | y)来获得更好的性能。但是关于max(ctz(x), ctz(y))

ctz表示“计数尾随零”。

C++ 版本(编译器资源管理器

#include <algorithm>
#include <bit>
#include <cstdint>

int32_t test2(uint64_t x, uint64_t y) {
    return std::max(std::countr_zero(x), std::countr_zero(y));
}
Run Code Online (Sandbox Code Playgroud)

Rust 版本(编译器资源管理器

pub fn test2(x: u64, y: u64) -> u32 {
    x.trailing_zeros().max(y.trailing_zeros())
}
Run Code Online (Sandbox Code Playgroud)

c++ algorithm bit-manipulation micro-optimization rust

33
推荐指数
3
解决办法
3184
查看次数

AVX2什么是基于面具打包左边最有效的方法?

如果你有一个输入数组和一个输出数组,但是你只想写那些通过某个条件的元素,那么在AVX2中这样做最有效的方法是什么?

我在SSE看到它是这样做的:(来自:https://deplinenoise.files.wordpress.com/2015/03/gdc2015_afredriksson_simd.pdf)

__m128i LeftPack_SSSE3(__m128 mask, __m128 val)
{
 // Move 4 sign bits of mask to 4-bit integer value.
 int mask = _mm_movemask_ps(mask);
 // Select shuffle control data
 __m128i shuf_ctrl = _mm_load_si128(&shufmasks[mask]);
 // Permute to move valid values to front of SIMD register
 __m128i packed = _mm_shuffle_epi8(_mm_castps_si128(val), shuf_ctrl);
 return packed;
}
Run Code Online (Sandbox Code Playgroud)

这对于4宽的SSE来说似乎很好,因此只需要16个入口LUT,但对于8宽的AVX,LUT变得非常大(256个条目,每个32个字节或8k).

我很惊讶AVX似乎没有简化此过程的指令,例如带有打包的蒙版存储.

我想通过稍微改变来计算左边设置的符号位数,你可以生成必要的排列表,然后调用_mm256_permutevar8x32_ps.但这也是我认为的一些指示......

有没有人知道用AVX2做这个的任何技巧?或者什么是最有效的方法?

以下是上述文件中左包装问题的说明:

Left.Packing.Problem

谢谢

c++ sse simd vectorization avx2

26
推荐指数
5
解决办法
6865
查看次数

设置任意大小整数的前导零位 C++

我想在标准 C++ 中将任何大小整数的前导零位设置为 1。

例如。

0001 0011 0101 1111 -> 1111 0011 0101 1111

我发现执行此操作的所有算法都需要相当昂贵的前导零计数。然而,这很奇怪。有非常快速且简单的方法来执行其他类型的位操作,例如:

 int y = -x & x; //Extracts lowest set bit, 1110 0101 -> 0000 0001

 int y = (x + 1) & x; //Will clear the trailing ones, 1110 0101 - > 1110 0100

 int y = (x - 1) | x; //Will set the trailing zeros, 0110 0100 - > 0110 0111
Run Code Online (Sandbox Code Playgroud)

因此,这让我认为必须有一种方法可以在由基本位运算符组成的一行简单代码中设置整数的前导零。请告诉我还有希望,因为现在我正在准备反转整数中的位顺序,然后使用设置尾随零的快速方法,然后再次反转整数以将前导零设置为 1。这实际上比使用前导零计数要快得多,但与上面的其他算法相比仍然相当慢。

 template<typename T>
 inline constexpr void reverse(T& x)
 {
    T rev …
Run Code Online (Sandbox Code Playgroud)

c++ optimization bit-manipulation dos x86-16

26
推荐指数
2
解决办法
2157
查看次数

如何在一个8位字节中对四个2位位域求和?

我有四个2位位域存储在一个字节中.因此,每个位域可以表示0,1,2或3.例如,以下是前3个位域为零的4个可能值:

00 00 00 00 = 0 0 0 0
00 00 00 01 = 0 0 0 1
00 00 00 10 = 0 0 0 2
00 00 00 11 = 0 0 0 3
Run Code Online (Sandbox Code Playgroud)

我想要一种有效的方法来对四个位域进行求和.例如:

11 10 01 00 = 3 + 2 + 1 + 0 = 6
Run Code Online (Sandbox Code Playgroud)

现代Intel x64 CPU上的8位查找表需要4个周期才能从L1返回答案.似乎应该有一些方法来比这更快地计算答案.3个周期为6-12个简单位操作提供了空间.作为首发,简单的面具和换挡在Sandy Bridge上需要5个周期:

假设位字段是:d c b a,并且该掩码是:00 00 00 11

从艾拉帮助澄清:这假设a,b,c,和d是相同的,都被设置为初始byte.奇怪的是,我想我可以免费做到这一点.因为我每循环可以做2次加载,而不是加载byte一次,我可以加载它四次:a …

c optimization assembly bit-manipulation bit-fields

10
推荐指数
2
解决办法
2128
查看次数

关于bsr和lzcnt的困惑

我对这两个指令都有点困惑.首先,让我们丢弃的特殊情况下,当扫描的值是0和未定义/ BSR或bitsize/lzcnt结果 - 这种差异是明显的,而不是我的问题的一部分.

我们来看二进制值 0001 1111 1111 1111 1111 1111 1111 1111

根据英特尔的规格,结果为lzcnt3

根据英特尔的规格,结果为bsr28

lzcntcount,bsr从位0返回索引或距离(即lsb).

两个指令如何相同,如何在CPU上没有可用的BMI的情况下lzcnt进行仿真bsr?或者bsr在msb的情况下是0位?英特尔规范中的"代码操作"也不同,一个是左边的计数或索引,另一个来自右边.

也许有人可以提供一些线索这光,我没有CPU无BMI/lzcnt指令测试,如果退回到bsr同样的结果作品(如值为0的特殊情况下扫描从未发生过).

x86 assembly bmi

8
推荐指数
2
解决办法
3085
查看次数