Я не знаю никакой реальной сборки, но могу прочитать вывод GCC -S для оценки фактических затрат на данный код C.
Этот вопрос не столько о профилировании и контрольных показателях, сколько об образовании. Мне нужно, чтобы кто-то объяснил мне, почему [1] фрагмент не быстрее второго.
Ну, привык думать: "Да, некоторые операции, такие как MUL, довольно дороги, но если одна сборка в X раз больше другой, она должна быть медленнее".
Это было верно, пока я не встретил этих двух:
unsigned char bytes[4] = {0, 0, 0, 5};
// 1
int32_t val = *((int32_t*)bytes);      
/* produces:
        leaq    -16(%rbp), %rax
        movl    (%rax), %eax
        movl    %eax, -4(%rbp)
        movl    $0, %eax
*/
// 2   
val = bytes[3] |                               
      (bytes[2] << 8) |                        
      (bytes[1] << 16) |
      (bytes[0] << 24);
/* produces: 
        movzbl  -13(%rbp), %eax
        movzbl  %al, %eax
        movzbl  -14(%rbp), %edx
        movzbl  %dl, %edx
        sall    $8, %edx
        orl     %eax, %edx
        movzbl  -15(%rbp), %eax
        movzbl  %al, %eax
        sall    $16, %eax
        orl     %eax, %edx
        movzbl  -16(%rbp), %eax
        movzbl  %al, %eax
        sall    $24, %eax
        orl     %edx, %eax
        movl    %eax, -4(%rbp)
        movl    $0, %eax
*/
И тесты показывают, что 2-й на 5-10% быстрее. Что здесь происходит?
Единственное существенное различие и "разум", которые я могу себе представить, - это LEAQ - это что-то очень медленное.
Последние 2 строки идентичны, поэтому, возможно, цена MOV настолько высока, что 1 дополнительный MOV хуже, чем тонны инструкций.
Вот что я использовал для измерения времени выполнения:
#include <stdio.h>
#include <time.h>
#include <stdint.h>
#define REPETITIONS 32
#define ITERATIONS 90000
#define CODE1                   \
  for (int i = 0; i < ITERATIONS; ++i) {    \
    val = *((int32_t*)bytes);           \
  }
#define CODE2                   \
  for (int i = 0; i < ITERATIONS; ++i) {    \
    val = bytes[3] |                \
      (bytes[2] << 8) |             \
      (bytes[1] << 16) |            \
      (bytes[0] << 24);             \
  }
int main(void) {
  clock_t minTs = 999999999, ts;
  unsigned char bytes[4] = {0, 0, 0, 5};    
  int32_t val;                  
  for (int i = 0; i < REPETITIONS; ++i) {
    ts = clock();
    CODE1; // Or CODE2
    ts = clock() - ts;
    if (ts < minTs) minTs = ts;
  }
  printf("ts: %ld\n", minTs);
  return val;
}
Обновить: как оказалось, результаты специфичны для аппаратного обеспечения, поэтому пока [1] работает медленнее на моем ноутбуке (x64 i5-4260U), он быстрее работает на моем ПК (но очень малой фракцией, например 5%).
