是否有用于 16 和/或 32 位值的 memset() 函数?

Posted

技术标签:

【中文标题】是否有用于 16 和/或 32 位值的 memset() 函数?【英文标题】:Is there a memset() function for 16 and/or 32 bit values? 【发布时间】:2019-04-19 14:05:06 【问题描述】:

memset() 非常快,因为它在内部受益于stosb 操作码。 是否有适用于 16 位和 32 位值的函数,可以有效地受益于 stosbstosw 和/或 stosd

wmemset() 不可移植,对 16 位值没有帮助。

【问题讨论】:

它适用于 256 个不同的 16 位和 32 位值,其中每个组件字节都具有相同的值,例如 0-1(如果是二进制补码)。 stoswstosdstosb 不同。在过去,所有这些指令都非常非常慢,因为它们在 uCode 级别上得到支持,并且为了向后兼容而保留。后来 Intel CPU 开始在硬件中实现stosb,以在硬件上提供高速memcpy,但stoswstosd 保持不变——基于uCode,因此非常慢。我认为没有任何变化,但我没有真正检查。 C++ 有::std::fill 编译器通常会以这种方式优化。 不幸的是我不能在我的项目中使用 C++ 函数。 你也可以阅读this关于stosbstosw/stosd之间区别的问题。这是我在之前的评论中谈到的。 【参考方案1】:

标准 C 中没有这样的功能,但根据您要执行的操作,您可能会使用特定于 CPU 的 SIMD“内在功能”来构建一个。您提到 stosb 让我觉得您使用的是 x86,因此请查看各种 *mmintrin.h 标头及其提供的功能的文档。

【讨论】:

是的,我在/usr/lib/gcc/x86_64-linux-gnu/7/include 中有这样的文件。但是我在哪里可以找到,我在寻找什么?你的意思是这样的:***.com/a/25579184/5399598 这是 32 位的。首先,我正在寻找 16 位的东西。 @CoSoCo 对不起,我已经帮不了你了。【参考方案2】:

是的,它有很多变体。

例如

void *memset16(void *m, uint16_t val, size_t count)

    uint16_t *buf = m;

    while(count--) *buf++ = val;
    return m;


void *memset32(void *m, uint32_t val, size_t count)

    uint32_t *buf = m;

    while(count--) *buf++ = val;
    return m;


void *memsetXX(void *m, void *val, size_t size, size_t count)

    char *buf = m;

    while(count--)
    
        memcpy(buf, val, size);
        buf += size;
    
    return m;

更安全的版本:

void *memset16safe(void *m, uint16_t val, size_t count)

    char *buf = m;
    union 
    
        uint8_t d8[2];
        uint16_t d16;
    u16 = .d16 = val;

    while(count--) 
    
        *buf++ = u16.d8[0];
        *buf++ = u16.d8[1];
    
    return m;


void *memset32(void *m, uint32_t val, size_t count)

    char *buf = m;
    union 
    
        uint8_t d8[4];
        uint32_t d32;
    u32 = .d32 = val;

    while(count--) 
    
        *buf++ = u32.d8[0];
        *buf++ = u32.d8[1];
        *buf++ = u32.d8[2];
        *buf++ = u32.d8[3];
    
    return m;

【讨论】:

人们必须小心这些(至少是固定宽度的变体),因为与常规的memset 不同,存在混叠违规的可能性。如果m 参数指向的对象与uint16_t 不兼容--或使用的任何类型--将调用未定义的行为。 @ChristianGibbons 我在这里看不到别名问题。对齐是 - 在某些情况下。 @P__J__ 假设一个变量声明为float fvec[N],那么memset32(&fvec, 0x7fe00000, N) 具有未定义的行为,即使floatuint32_t 具有相同的大小和对齐要求。 我不明白,为什么这些函数可以像使用普通循环一样更快,例如:uint16_t fill = pattern; for (int x=count; x >= --x; ) data[x] = fill; 它和uint8_t fill = pattern; 一样慢@ 在 8 位的情况下,我可以将其替换为非常快的 memset() 函数。但在 16 位的情况下,我错过了/不知道这么快的可能性。

以上是关于是否有用于 16 和/或 32 位值的 memset() 函数?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 16 位值的数组转换为 base64?

在 OpenCv Mat 中钳位值的最有效方法

使用 SIMD 根据另一个向量位值计算值的乘积

使用 SPI 从 STM32 上的磁性编码器传输和读取 16 位值

优化 32 位值构造

SSE指令:字节+短