相关疑难解决方法(0)

并行地将 64 位整数中的压缩 8 位整数减去 1,SWAR 没有硬件 SIMD

如果我有一个 64 位整数,我将其解释为一个包含 8 个元素的压缩 8 位整数数组。我需要1在处理溢出时从每个压缩整数中减去常量,而一个元素的结果不会影响另一个元素的结果。

我现在有这个代码并且它可以工作,但我需要一个解决方案来并行地减去每个打包的 8 位整数并且不进行内存访问。在 x86 上,我可以使用类似的 SIMD 指令psubb并行减去打包的 8 位整数,但我正在编码的平台不支持 SIMD 指令。(在这种情况下为 RISC-V)。

因此,我正在尝试执行SWAR(寄存器内的 SIMD)以手动取消 a 的字节之间的进位传播uint64_t,执行与此等效的操作:

uint64_t sub(uint64_t arg) {
    uint8_t* packed = (uint8_t*) &arg;

    for (size_t i = 0; i < sizeof(uint64_t); ++i) {
        packed[i] -= 1;
    }

    return arg;
}
Run Code Online (Sandbox Code Playgroud)

我认为你可以用按位运算符来做到这一点,但我不确定。我正在寻找一种不使用 SIMD 指令的解决方案。我正在寻找一个非常便携的 C 或 C++ 解决方案,或者只是它背后的理论,这样我就可以实现我自己的解决方案。

c c++ bit-manipulation simd swar

79
推荐指数
5
解决办法
5017
查看次数

启用优化后,为什么此代码慢6.5倍?

我想基准glibcstrlen功能,出于某种原因,发现它显然执行慢与GCC启用优化,我不知道为什么。

这是我的代码:

#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

int main() {
    char *s = calloc(1 << 20, 1);
    memset(s, 65, 1000000);
    clock_t start = clock();
    for (int i = 0; i < 128; ++i) {
        s[strlen(s)] = 'A';
    }
    clock_t end = clock();
    printf("%lld\n", (long long)(end - start));
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在我的机器上,它输出:

$ gcc test.c && ./a.out
13336
$ gcc -O1 test.c && ./a.out
199004
$ gcc -O2 test.c && ./a.out
83415 …
Run Code Online (Sandbox Code Playgroud)

c performance gcc glibc

64
推荐指数
2
解决办法
3997
查看次数

gcc,严格别名和恐怖故事

gcc-strict-aliasing-and-casting-through-a-union中,我问是否有人遇到过通过指针进行联合惩罚的问题.到目前为止,答案似乎是否定的.

这个问题是广泛的:你有任何关于gcc和严格走样恐怖故事?

背景:引用AndreyT在c99-strict-aliasing-rules-in-c-gcc中的答案:

"严格的别名规则植根于自[标准化]时代开始以来C和C++中存在的标准部分.禁止通过另一种类型的左值访问一种类型的对象的条款存在于C89/90中(6.3 )以及C++ 98(3.10/15)......并非所有编译器都希望(或敢于)强制执行或依赖它.

好吧,gcc现在敢于用它的-fstrict-aliasing开关来做到这一点.这引起了一些问题.例如,请参阅有关Mysql错误的优秀文章 http://davmac.wordpress.com/2009/10/,以及http://cellperformance.beyond3d.com/articles/2006/06/understanding中同样出色的讨论.-strict-aliasing.html.

其他一些不太相关的链接:

重复一遍,你有自己的恐怖故事吗?当然,没有表示的问题-Wstrict-aliasing是优选的.其他C编译器也很受欢迎.

6月2日补充:迈克尔伯尔的答案中的第一个链接,确实有资格作为恐怖故事,可能有点过时(从2003年开始).我做了一个快速测试,但问题显然已经消失了.

资源:

#include <string.h>
struct iw_event {               /* dummy! */
    int len;
};
char *iwe_stream_add_event(
    char *stream,               /* Stream of events */
    char *ends,                 /* End of stream */
    struct iw_event *iwe,       /* Payload */
    int event_len)              /* Real size of payload …
Run Code Online (Sandbox Code Playgroud)

c gcc strict-aliasing

51
推荐指数
4
解决办法
3万
查看次数

在x86和x64上读取同一页面内的缓冲区末尾是否安全?

如果允许在输入缓冲区末尾读取少量数据,则可以(并且)简化在高性能算法中找到的许多方法.这里,"少量"通常意味着W - 1超过结束的字节,其中W是算法的字节大小(例如,对于处理64位块中的输入的算法,最多7个字节).

很明显,写入输入缓冲区的末尾通常是不安全的,因为您可能会破坏缓冲区1之外的数据.同样清楚的是,在缓冲区的末尾读取到另一页面可能会触发分段错误/访问冲突,因为下一页可能不可读.

但是,在读取对齐值的特殊情况下,页面错误似乎是不可能的,至少在x86上是这样.在该平台上,页面(以及因此内存保护标志)具有4K粒度(较大的页面,例如2MiB或1GiB,可能,但这些是4K的倍数),因此对齐的读取将仅访问与有效页面相同的页面中的字节缓冲区的一部分.

这是一个循环的规范示例,它对齐其输入并在缓冲区末尾读取最多7个字节:

int processBytes(uint8_t *input, size_t size) {

    uint64_t *input64 = (uint64_t *)input, end64 = (uint64_t *)(input + size);
    int res;

    if (size < 8) {
        // special case for short inputs that we aren't concerned with here
        return shortMethod();
    }

    // check the first 8 bytes
    if ((res = match(*input)) >= 0) {
        return input + res;
    }

    // align pointer to the next 8-byte …
Run Code Online (Sandbox Code Playgroud)

c optimization performance x86 assembly

33
推荐指数
2
解决办法
2027
查看次数

索引一个 `unsigned long` 变量并打印结果

昨天,有人给我看了这个代码:

#include <stdio.h>

int main(void)
{
    unsigned long foo = 506097522914230528;
    for (int i = 0; i < sizeof(unsigned long); ++i)
        printf("%u ", *(((unsigned char *) &foo) + i));
    putchar('\n');

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这导致:

0 1 2 3 4 5 6 7
Run Code Online (Sandbox Code Playgroud)

我很困惑,主要是for循环中的行。据我所知,似乎&foo是被强制转换为 anunsigned char *然后被i. 我觉得*(((unsigned char *) &foo) + i)是一个更详细的书写方式((unsigned char *) &foo)[i],但是这使得它看起来像foo,一个unsigned long被索引。如果是这样,为什么?循环的其余部分似乎是典型的打印数组的所有元素,所以一切似乎都表明这是真的。演员unsigned char *阵容让我更加困惑。我试图寻找有关转换的整数类型,以char *对谷歌而言,但我的研究得到了一些后无用的搜索结果停留约铸造int …

c pointers casting char-pointer implementation-defined-behavior

31
推荐指数
2
解决办法
1182
查看次数

关于C++中类型惩罚的观点?

我很好奇C++中类型惩罚指针/数组的约定.这是我目前的用例:

通过将其视为32位整数数组(我们知道它的总长度是4的倍数),然后将所有值相加并忽略溢出,计算二进制blob数据的简单32位校验和.

我希望这样的函数看起来像这样:

uint32_t compute_checksum(const char *data, size_t size)
{
    const uint32_t *udata = /* ??? */;
    uint32_t checksum = 0;
    for (size_t i = 0; i != size / 4; ++i)
        checksum += udata[i];
    return udata;
 }
Run Code Online (Sandbox Code Playgroud)

现在我的问题是,您认为转换data为"最佳"的方式是udata什么?

C风格演员?

udata = (const uint32_t *)data
Run Code Online (Sandbox Code Playgroud)

假设所有指针都是可转换的C++强制转换?

udata = reinterpret_cast<const uint32_t *>(data)
Run Code Online (Sandbox Code Playgroud)

C++在任意指针类型之间使用中间转换void*

udata = static_cast<const uint32_t *>(static_cast<const void *>(data))
Run Code Online (Sandbox Code Playgroud)

通过工会铸造?

union {
    const uint32_t *udata;
    const char *cdata;
};
cdata = data;
// now …
Run Code Online (Sandbox Code Playgroud)

c++ casting type-punning

22
推荐指数
2
解决办法
8572
查看次数

严格的别名规则和'char*'指针

什么是严格别名规则的接受答案提到您可以使用char *别名而不是其他方式.

这对我来说没有意义 - 如果我们有两个指针,一个是指向同一个位置的类型char *,另一个struct something *指向同一个位置,那么第一个别名可能是第二个但第二个不是第一个别名吗?

c c++ strict-aliasing

20
推荐指数
2
解决办法
4115
查看次数

为什么CPU在字边界上访问内存?

我听到很多数据应该在内存中正确对齐,以提高访问效率.CPU访问内存在字边界上.

因此,在以下场景中,CPU必须进行2次内存访问才能获得单个字.

Supposing: 1 word = 4 bytes

("|" stands for word boundary. "o" stands for byte boundary)


|----o----o----o----|----o----o----o----|   (The word boundary in CPU's eye)
           ----o----o----o----              (What I want to read from memory)
Run Code Online (Sandbox Code Playgroud)

为什么会这样?什么是CPU的根本原因只能读取字边界?

如果CPU只能访问4字节字边界,则地址线应仅需要30位,而不是32位宽.因为CPU的眼中最后2位始终为0.

添加1

更重要的是,如果我们承认CPU必须读取字边界,为什么边界不能我想要读取的地方开始?似乎边界在CPU眼中是固定的.

添加2

根据AndreyT的说法,似乎边界设置是硬连线的,它是由内存访问硬件硬连线的.就这一点而言,CPU是无辜的.

非常感谢...

c memory cpu assembly operating-system

19
推荐指数
2
解决办法
5349
查看次数

我可以使用 SIMD 来加速字符串操作吗?

SIMD 指令是否仅用于矢量数值计算?或者它是否适合一类字符串操作任务,例如将数据行写入文本文件,其中行的顺序无关紧要?如果是这样,我应该从哪些 API 或库开始?

c c++ string optimization simd

8
推荐指数
3
解决办法
672
查看次数

是否由于未定义的行为导致错位负载?

是否由于void*未定义的行为导致错位负载?


以下是我对Clang及其消毒剂的看法:

bufhelp.h:146:29: runtime error: load of misaligned address 0x7fff04fdd0e1 for type 'const uintptr_t' (aka 'const unsigned long'), which requires 8 byte alignment
0x7fff04fdd0e1: note: pointer points here
 00 00 00  66 66 6f 6f 62 61 72 34  32 46 4f 4f 42 41 52 31  37 66 6f 6f 62 61 72 34  33 46 4f 4f 42
              ^ 
Run Code Online (Sandbox Code Playgroud)

这是演员阵容发挥作用的地方:

buf_xor(void *_dst, const void *_src1, const void *_src2, size_t len)
{
  ...
  ldst = (uintptr_t *)(void …
Run Code Online (Sandbox Code Playgroud)

c casting memory-alignment undefined-behavior

7
推荐指数
1
解决办法
2810
查看次数