相关疑难解决方法(0)

如何摆脱边界检查

有没有办法删除数组边界检查C#

这是我想要实现的目标:

public static int F(int[] M, int i) 
{
    return M[i]; // I can guarantee that [i] will never be outside of [0, M.Length]
}
Run Code Online (Sandbox Code Playgroud)

在这个函数调用之前,我有一个逻辑已经检查了边界(其中有一些额外的逻辑)。我要删除的内容如下:

Program.F(Int32[], Int32)
    L0000: sub rsp, 0x28
    L0004: cmp edx, [rcx+8]           ; I don't need this line
    L0007: jae short L0015            ; I don't need this line
    L0009: movsxd rax, edx
    L000c: mov eax, [rcx+rax*4+0x10]
    L0010: add rsp, 0x28
    L0014: ret
    L0015: call 0x00007ffc8877bc70    ; I don't need this line
    L001a: int3 …
Run Code Online (Sandbox Code Playgroud)

c# arrays assembly x86-64

9
推荐指数
1
解决办法
2708
查看次数

错误的gcc生成的装配顺序,导致性能损失

我有以下代码,它将数据从内存复制到DMA缓冲区:

for (; likely(l > 0); l-=128)
{
    __m256i m0 = _mm256_load_si256( (__m256i*) (src) );
    __m256i m1 = _mm256_load_si256( (__m256i*) (src+32) );
    __m256i m2 = _mm256_load_si256( (__m256i*) (src+64) );
    __m256i m3 = _mm256_load_si256( (__m256i*) (src+96) );

    _mm256_stream_si256( (__m256i *) (dst), m0 );
    _mm256_stream_si256( (__m256i *) (dst+32), m1 );
    _mm256_stream_si256( (__m256i *) (dst+64), m2 );
    _mm256_stream_si256( (__m256i *) (dst+96), m3 );

    src += 128;
    dst += 128;
}
Run Code Online (Sandbox Code Playgroud)

这就是gcc程序集输出的样子:

405280:       c5 fd 6f 50 20          vmovdqa 0x20(%rax),%ymm2
405285:       c5 …
Run Code Online (Sandbox Code Playgroud)

c optimization performance assembly gcc

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

C循环优化有助于最终分配

因此,对于我在计算机系统课程中的最终作业,我们需要优化这些forloops,使其比原始版本更快.使用我们的linux服务器,基本等级不到7秒,完整等级不到5秒.我在这里的代码大约需要5.6秒.我想我可能需要以某种方式使用指针来使它更快,但我不是很确定.任何人都可以提供我的任何提示或选项吗?非常感谢!

QUICKEDIT:文件必须保持50行或更少,我忽略了教师所包含的那些注释行.

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

// You are only allowed to make changes to this code as specified by the comments in it.

// The code you submit must have these two values.
#define N_TIMES     600000
#define ARRAY_SIZE   10000

int main(void)
{
    double  *array = calloc(ARRAY_SIZE, sizeof(double));
    double  sum = 0;
    int     i;

    // You can add variables between this comment ...
    register double sum1 = 0, sum2 = 0, sum3 = 0, sum4 = 0, sum5 = 0, …
Run Code Online (Sandbox Code Playgroud)

c optimization loops compiler-optimization debug-mode

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

跳回矢量化余数循环的一些迭代

我手动矢量化循环并一次处理4个项目.项目总数可能不是4的倍数,所以我在主循环结束时留下了一些项目.我认为如果计数大于4并且重做某些项目是安全的,我可以使用相同的主矢量化循环执行剩余项目.例如,如果我需要处理10个项目,我可以在三次迭代中处理0123,4567,6789.我找不到任何对这种技术的引用.它是愚蠢但我不明白怎么样?

#include <stdint.h>
#include <stddef.h>

void inc(int32_t const* __restrict in, int32_t* out, size_t count)
{
    if (count < 4)
    {
        for (size_t i = 0; i < count; ++i)
            out[i] = in[i] + 42;
    }
    else
    {
        typedef int32_t v4i __attribute__ ((vector_size (16), aligned(4)));
        for (size_t i = 0;;)
        {
            for (; i + 4 <= count; i += 4)
            {
                (v4i&)out[i] = (v4i&)in[i] + 42;
            }
            if (i == count)
                break;
            i = count - 4;
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

c performance x86 assembly simd

8
推荐指数
1
解决办法
117
查看次数

在预测现代超标量处理器上的操作延迟时需要考虑哪些因素以及如何手动计算它们?

我希望能够手动预测任意算术的长度(即没有分支或内存,尽管这也很好)x86-64汇编代码将采用特定的体系结构,考虑到指令重新排序,超标量,延迟,消费者价格指数等

什么/描述必须遵循的规则才能实现这一目标?


我想我已经找到了一些初步规则,但是我没有找到任何关于将任何示例代码分解为这个详细程度的引用,所以我不得不做一些猜测.(例如,英特尔优化手册甚至几乎没有提到指令重新排序.)

至少,我正在寻找(1)确认每条规则是正确的,或者是每条规则的正确陈述,以及(2)我可能忘记的任何规则的列表.

  • 每个循环发出尽可能多的指令,从当前循环开始按顺序开始,并且可能与重新排序缓冲区大小一样远.
  • 如果出现以下情况,可以在给定周期发出指令:
    • 没有影响其操作数的指令仍在执行中.和:
    • 如果它是浮点指令,则它之前的每个浮点指令都被发出(浮点指令具有静态指令重新排序).和:
    • 该循环有一个功能单元可用于该指令.每个(?)功能单元是流水线的,这意味着它可以在每个周期接受1个新指令,并且对于给定功能类的CPI,总功能单元的数量是1/CPI(这里模糊不清:可能是例如addps并且subps使用相同的功能) unit?我如何确定?).和:
    • 4此循环已经发出少于超标量宽度(通常)指令的数量.
  • 如果不能发出指令,则处理器不会发出任何称为"停顿"的条件.

例如,请考虑以下示例代码(计算交叉产品):

shufps   xmm3, xmm2, 210
shufps   xmm0, xmm1, 201
shufps   xmm2, xmm2, 201
mulps    xmm0, xmm3
shufps   xmm1, xmm1, 210
mulps    xmm1, xmm2
subps    xmm0, xmm1
Run Code Online (Sandbox Code Playgroud)

我试图预测Haswell的延迟看起来像这样:

; `mulps`  Haswell latency=5, CPI=0.5
; `shufps` Haswell latency=1, CPI=1
; `subps`  Haswell latency=3, CPI=1

shufps   xmm3, xmm2, 210   ; cycle  1
shufps   xmm0, xmm1, 201   ; cycle  2
shufps   xmm2, xmm2, 201   ; …
Run Code Online (Sandbox Code Playgroud)

assembly pipeline latency x86-64 superscalar

8
推荐指数
1
解决办法
268
查看次数

计算两个缓冲区之间的差异似乎太慢

我的问题

我有 2 个相邻的大小相同的字节缓冲区(每个缓冲区大约 20 MB)。我只是想数一下它们之间的差异。

我的问题

该循环在具有 3600MT RAM 的 4.8GHz Intel I7 9700K 上运行需要多长时间?

我们如何计算最大理论速度?

我尝试过的

uint64_t compareFunction(const char *const __restrict buffer, const uint64_t commonSize)
{
    uint64_t diffFound = 0;

    for(uint64_t byte = 0; byte < commonSize; ++byte)
        diffFound += static_cast<uint64_t>(buffer[byte] != buffer[byte + commonSize]);

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

在我的电脑(9700K 4.8Ghz RAM 3600 Windows 10 Clang 14.0.6 -O3 MinGW)上需要 11 毫秒,我觉得它太慢了,而且我错过了一些东西。

CPU 读取 40MB 的时间应该少于 2 毫秒(我的 RAM 带宽在 20 到 30GB/s 之间)

我不知道如何计算执行一次迭代所需的周期(特别是因为现在的 CPU 是超标量)。如果我假设每个操作有 …

optimization performance x86 assembly clang++

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

访问打包在128位寄存器中的任意16位元素

使用英特尔编译器内在函数,给定一个128位寄存器,打包8个16位元素,如何从寄存器中访问(廉价)任意元素,以便后续使用_mm_cvtepi8_epi64(符号扩展两个8位元素,打包在较低位置) 16位寄存器,两个64位元素)?


我会解释为什么我问:

  1. 输入:具有k个字节的内存缓冲区,每个字节为0x0或0xff.
  2. 期望输出:对于输入的每两个连续字节,寄存器分别用0x0和包装两个四字(64位)0xffff ffff ffff ffff.
  3. 最终目标:根据输入缓冲区的条目对k个双精度缓冲区进行求和.

注意:输入缓冲区的值0x0和值0xff可以更改为最有用的值,前提是在总和之前屏蔽效果仍然存在.

从我的问题可以明显看出,我目前的计划如下,在输入缓冲区中流式传输:

  1. 将输入掩码缓冲区从8位扩展到64位.
  2. 使用扩展掩码屏蔽双打缓冲区.
  3. 总结蒙面的双打.

谢谢,阿萨夫

assembly sse simd intrinsics micro-optimization

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

如果没有快速收集和分散AVX2指令,你会怎么做?

我正在编写一个程序来检测素数.其中一部分是筛选可能的候选人.我写了一个相当快的程序,但我想我会看到是否有人有更好的想法.我的程序可以使用一些快速收集和分散指令,但我只限于AVX2硬件用于x86架构(我知道AVX-512有这些但我不确定它们有多快).

#include <stdint.h>
#include <immintrin.h>

#define USE_AVX2

// Sieve the bits in array sieveX for later use
void sieveFactors(uint64_t *sieveX)
{
    const uint64_t totalX = 5000000;
#ifdef USE_AVX2
    uint64_t indx[4], bits[4];

    const __m256i sieveX2 = _mm256_set1_epi64x((uint64_t)(sieveX));
    const __m256i total = _mm256_set1_epi64x(totalX - 1);
    const __m256i mask = _mm256_set1_epi64x(0x3f);

    // Just filling with some typical values (not really constant)
    __m256i ans = _mm256_set_epi64x(58, 52, 154, 1);
    __m256i ans2 = _mm256_set_epi64x(142, 70, 136, 100);

    __m256i sum = _mm256_set_epi64x(201, 213, 219, 237);    // …
Run Code Online (Sandbox Code Playgroud)

algorithm optimization performance simd avx2

7
推荐指数
2
解决办法
1460
查看次数

复杂的寻址模式对于从内存加载是否有额外的开销?

这些加载指令之间的性能有差异吗mov?与简单的寻址模式相比,更复杂的寻址模式是否有额外的开销(延迟或吞吐量)?

# AT&T syntax                       # Intel syntax:
movq (%rsi), %rax                   mov  rax, [rsi]

movq (%rdi, %rsi), %rax             mov  rax, [rdi + rsi]

movq (%rdi, %rsi, 4), %rax          mov  rax, [rdi + rsi*4]
Run Code Online (Sandbox Code Playgroud)

performance x86 assembly micro-optimization

6
推荐指数
1
解决办法
1445
查看次数

计算__m256i字中的前导零

我正在修改AVX-2指令,我正在寻找一种快速计算__m256i单词中前导零数(具有256位)的方法.

到目前为止,我已经找到了以下方法:

// Computes the number of leading zero bits.
// Here, avx_word is of type _m256i.

if (!_mm256_testz_si256(avx_word, avx_word)) {
  uint64_t word = _mm256_extract_epi64(avx_word, 0);
  if (word > 0)
    return (__builtin_clzll(word));

  word = _mm256_extract_epi64(avx_word, 1);
  if (word > 0)
    return (__builtin_clzll(word) + 64);

  word = _mm256_extract_epi64(avx_word, 2);
  if (word > 0)
    return (__builtin_clzll(word) + 128);

  word = _mm256_extract_epi64(avx_word, 3);
  return (__builtin_clzll(word) + 192);
} else
  return 256; // word is entirely zero
Run Code Online (Sandbox Code Playgroud)

但是,我发现在256位寄存器中找出确切的非零字是相当笨拙的.

有人知道是否有更优雅(或更快)的方法吗?

正如附加信息:我实际上想要计算由逻辑AND创建的任意长向量的第一个设置位的索引,并且我将标准64位操作的性能与SSE和AVX-2代码进行比较.这是我的整个测试代码:

#include <stdio.h> …
Run Code Online (Sandbox Code Playgroud)

c x86 simd intrinsics avx

6
推荐指数
1
解决办法
871
查看次数