标签: micro-optimization

MySQL SELECTS的开销 - 更好地使用一个或多个顺序

在一个SELECT foo, bar, FROM users查询返回500行和500个SELECT foo, bar, FROM users WHERE id = x查询同时出现之间是否存在明显的性能差异?

在我正在编写的PHP应用程序中,我正在尝试在编写清晰,可读的代码段之间进行选择,这将产生大约500个SELECT语句; 或者以一种模糊,复杂的方式编写它,只使用一个返回500行的SELECT.

我更喜欢使用清晰,可维护代码的方式,但我担心每个SELECT的连接开销都会导致性能问题.

背景信息,如果它是相关的:1)这是一个Drupal模块,用PHP编码2)有问题的表获得很少的INSERT和UPDATE,很少被锁定3)SQL JOIN不可能出于与之无关的原因题

谢谢!

php mysql performance overhead micro-optimization

4
推荐指数
1
解决办法
1553
查看次数

x86 操作码对齐参考和指南

我正在 JIT 编译器中动态生成一些操作码,我正在寻找操作码对齐的指南。

1)我已经阅读了通过在调用后添加 nops 来简要“推荐”对齐的评论

2)我还阅读了有关使用 nop 优化并行性序列的信息。

3)我读过操作对齐对“缓存”性能有好处

通常这些评论不提供任何支持性参考。阅读博客或评论说“这样做这样那样是个好主意”是一回事,但实际编写一个编译器来实现特定的操作序列并在线实现大多数材料,尤其是博客,这是另一回事用于实际应用。所以我相信自己找出问题(反汇编等,看看现实世界的应用程序做了什么)。这是我需要一些外部信息的一种情况。

我注意到编译器通常会在之前的任何指令序列之后立即启动一个奇字节指令。因此,在大多数情况下,编译器不会特别注意。我在这里或那里看到“nop”,但通常似乎 nop 被谨慎使用,如果有的话。操作码对齐有多重要?您能否提供我可以实际用于实施的案例的参考资料?谢谢。

x86 assembly x86-64 memory-alignment micro-optimization

4
推荐指数
1
解决办法
1060
查看次数

为什么 Linux 上的 NASM 会更改 x86_64 程序集中的寄存器

我是 x86_64 汇编编程的新手。我正在用 x86_64 程序集编写简单的“Hello World”程序。下面是我的代码,它运行得很好。

global _start

section .data

    msg: db "Hello to the world of SLAE64", 0x0a
    mlen equ $-msg

section .text
    _start:
            mov rax, 1
            mov rdi, 1
            mov rsi, msg
            mov rdx, mlen
            syscall

            mov rax, 60
            mov rdi, 4
            syscall 
Run Code Online (Sandbox Code Playgroud)

现在,当我在 gdb 中反汇编时,它会给出以下输出:

(gdb) disas
Dump of assembler code for function _start:
=> 0x00000000004000b0 <+0>:     mov    eax,0x1
   0x00000000004000b5 <+5>:     mov    edi,0x1
   0x00000000004000ba <+10>:    movabs rsi,0x6000d8
   0x00000000004000c4 <+20>:    mov    edx,0x1d
   0x00000000004000c9 <+25>:    syscall
   0x00000000004000cb <+27>:    mov …
Run Code Online (Sandbox Code Playgroud)

assembly x86-64 nasm micro-optimization shellcode

4
推荐指数
2
解决办法
1274
查看次数

通过C中的循环阻塞进行优化

我目前正在研究C优化,并且过去的任务优化了一段代码.在其他优化(展开循环和强度降低)中,我根据缓存大小使用了阻塞(遵循英特尔关于此事的教程):

https://software.intel.com/en-us/articles/how-to-use-loop-blocking-to-optimize-memory-use-on-32-bit-intel-architecture.

现在我想我理解为什么这种技术在这种情况下工作,其中步幅为1,它加载到缓存块中并减少访问内存中下一个位置时的未命中数.但是在我的代码中dst[dim * jj + ii]似乎遍布整个地方,因为它jj在最里面的循环中被乘以.缓存是如何解释的?dim乘以0然后是1然后2等在某个时刻它将超过块可以容纳的并且优化将是毫无意义的.我明白了吗?

然而在实践中,当我只用于拦截jj可变我没有得到加速性能我使用阻塞都没有iijj.所以我把它做得更快但不知道为什么.作业现在已经过去了,但我仍然不明白,而且非常令人沮丧.提前感谢您提出可能是一个非常愚蠢的问题.

   void transpose(int *dst, int *src, int dim)
   {
      int i, j, dimi, jj,ii;
      dimi = 0;
      for(i=0; i < dim; i+=block_size)
      {
        for(j=0; j<dim; j+=block_size)
        {
          for(ii = i; ii < i+block_size; ii++)
          {
            dimi = dim * ii;
            for(jj = j; jj < j+block_size; jj++)
            {
              dst[dim*jj + ii] =  src[dimi + jj];
            }
          }
        }
      }
    }
Run Code Online (Sandbox Code Playgroud)

c performance micro-optimization cpu-cache

4
推荐指数
1
解决办法
1032
查看次数

是否有任何内置函数可以告诉编译器分支是否可预测?

我不是在问类似的事情__builtin_expect。我在考虑一种情况,即我不知道分支通常是对还是错,但是我确实知道分支是可预测的(或不是可预测的)。

我希望编译器在知道分支是可预测的之后,更有可能生成分支,并且知道它是不可预测的,因此更有可能在没有分支的情况下生成有条件执行的指令。

在主要编译器中可能吗?(专门考虑gcc和clang)。


解释“可预测”和“可能”为何不同的示例

int x = rand()%2;
while (true) {
    if (x) {
        // do something
    }
}
Run Code Online (Sandbox Code Playgroud)

if声明既不太可能也不太可能,但可以高度预测。

while (true) {
    if (rand()%5 > 0) {
        // do something
    }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,情况恰恰相反:分支很有可能(占80%的时间),但不可预测。

c c++ gcc micro-optimization llvm-clang

4
推荐指数
1
解决办法
141
查看次数

为什么_umul128比mul128x64x2函数的标量代码慢?

我第二次尝试实现mul128x64x2快速功能。第一次我问这个问题而没有与_umul128 MSVC版本进行比较。现在,我进行了这样的比较,结果表明_umul128函数的速度比本机标量和​​手工simd AVX 1.0代码慢。

在我的测试代码下面:

#include <iostream>
#include <chrono>

#include <intrin.h>
#include <emmintrin.h>
#include <immintrin.h>

#pragma intrinsic(_umul128)

constexpr uint32_t LOW[4] = { 4294967295u, 0u, 4294967295u, 0u };

__forceinline void multiply128x128( const uint32_t ABCD[4], const uint32_t EFGH[4], uint32_t OUT[2][4] ) noexcept
{
    __m128i L  = _mm_lddqu_si128( reinterpret_cast< __m128i const* >( LOW ) );
    __m128i IN = _mm_lddqu_si128( reinterpret_cast< __m128i const* >( EFGH ) );

    __m128i A  = _mm_set1_epi32( ABCD[0] );
    __m128i B  = _mm_set1_epi32( ABCD[1] );
    __m128i C  = …
Run Code Online (Sandbox Code Playgroud)

c++ x86 simd avx micro-optimization

4
推荐指数
1
解决办法
115
查看次数

如何指示 MS Visual C++ 编译器使用未初始化的 __m512i 寄存器

如何指示 Visual C++ 编译器 (1926) 使用未初始化的__m512i寄存器。在下面的代码片段not(or(A,B))中计算了a ,内容dummy无关紧要。

__m512i dummy;
const __m512i n8 = _mm512_ternarylogic_epi64(dummy, A, B, 0x11);
Run Code Online (Sandbox Code Playgroud)

不知何故,编译器假定寄存器需要有一些内容,(它没有),并为以下生成昂贵且不必要的内存引用zmm0

62 F1 7E 48 6F 45 00 vmovdqu32   zmm0,zmmword ptr [rbp]  
62 F3 DD 48 25 C5 11 vpternlogq  zmm0,zmm4,zmm5,11h  
Run Code Online (Sandbox Code Playgroud)

ICC 19.0.1 了解这种情况并且不会生成vmovdqu32.

我试过什么:dummy用 0初始化替换为vmovdqu32

C5 F1 EF C9          vpxor       xmm1,xmm1,xmm1
Run Code Online (Sandbox Code Playgroud)

这仍然给出了不必要的指令和停顿。

因此问题是:如何指示 Visual C++ 编译器执行与 Intel 编译器相同的操作?只是不要初始化虚拟寄存器。

c++ intrinsics micro-optimization visual-c++ avx512

4
推荐指数
1
解决办法
91
查看次数

为什么在 gcc 9.1 中的这个不必要的 MOVAPD 副本,在一个小函数中

考虑以下代码:

double x(double a,double b) {
    return a*(float)b;
}
Run Code Online (Sandbox Code Playgroud)

它做了一个转换形式doublefloat然后再double乘以。

当我gcc 9.1-O3on编译它时,x86/64我得到:

x(double, double):
        movapd  xmm2, xmm0
        pxor    xmm0, xmm0
        cvtsd2ss        xmm1, xmm1
        cvtss2sd        xmm0, xmm1
        mulsd   xmm0, xmm2
        ret
Run Code Online (Sandbox Code Playgroud)

使用clang和旧版本的gcc我得到这个:

x(double, double):
        cvtsd2ss        xmm1, xmm1
        cvtss2sd        xmm1, xmm1
        mulsd   xmm0, xmm1
        ret
Run Code Online (Sandbox Code Playgroud)

在这里我不抄xmm0xmm2,这似乎不需要我。

随着gcc 9.1-Os我得到:

x(double, double):
        movapd  xmm2, xmm0
        cvtsd2ss        xmm1, xmm1
        cvtss2sd        xmm0, …
Run Code Online (Sandbox Code Playgroud)

assembly gcc sse x86-64 micro-optimization

4
推荐指数
1
解决办法
93
查看次数

将 __m256i 寄存器转换为 uint64_t 位掩码,使得每个字节的值是输出中的一个设置位

基本上我有一个__m256i变量,其中每个字节代表一个需要在uint64_t. 请注意,所有字节值都将 < 64。

我对如何远程有效地做到这一点感到有些茫然。

我正在考虑的一种选择是在某些情况下字节之间有很多重复项,因此类似于:

__m256i indexes = foo();

uint64_t result         = 0;
uint32_t aggregate_mask = ~0;
do {
    uint32_t idx = _mm256_extract_epi8(indexes, __tzcnt_u32(aggregate_mask));

    uint32_t idx_mask =
        _mm256_movemask_epi8(_mm256_cmpeq_epi(indexes, _mm256_set1_epi8(idx)));
    aggregate_mask ^= idx_mask;
    result |= ((1UL) << idx);
} while (aggregate_mask);
Run Code Online (Sandbox Code Playgroud)

有了足够多的重复项,我相信这可能会有些效率,但我不能保证总是有足够的重复项来使这比仅遍历字节并按顺序设置更快。

我的目标是找到一些东西,这总是比感觉最坏的情况要快:

__m256i indexes = foo();
uint8_t index_arr[32];
_mm256_store_si256((__m256i *)index_arr, indexes);

uint64_t result = 0;
for (uint32_t i = 0; i < 32; ++i) {
    result |= ((1UL) << index_arr[i];
}
Run Code Online (Sandbox Code Playgroud)

如果可能,我正在寻找可以在 Skylake (wo AVX512) …

c++ simd avx micro-optimization avx2

4
推荐指数
1
解决办法
461
查看次数

长度更改前缀 (LCP) 是否会导致简单 x86_64 指令停顿?

考虑一个简单的指令,例如

mov RCX, RDI          # 48 89 f9
Run Code Online (Sandbox Code Playgroud)

48 是 x86_64 的 REX 前缀。它不是LCP。但请考虑添加 LCP(用于对齐目的):

.byte 0x67
mov RCX, RDI          # 67 48 89 f9
Run Code Online (Sandbox Code Playgroud)

67 是地址大小前缀,在本例中用于没有地址的指令。该指令也没有立即数,并且不使用 F7 操作码(假 LCP 停止;F7 将是 TEST、NOT、NEG、MUL、IMUL、DIV + IDIV)。假设它也不跨越 16 字节边界。这些是 Intel优化参考手册中提到的 LCP 停顿情况。

该指令是否会导致 LCP 停顿(在 Skylake、Haswell 等上)?两个 LCP 怎么样?

我日常驾驶的是 MacBook。所以我无法访问 VTune,也无法查看 ILD_STALL 事件。还有其他方法可以知道吗?

performance assembly x86-64 cpu-architecture micro-optimization

4
推荐指数
1
解决办法
669
查看次数