Inizialmente esaminando l'effetto della direttiva #pragma omp simd
, mi sono imbattuto in un comportamento che non riesco a spiegare, relativo alla vettorizzazione di un ciclo for semplice. Il seguente esempio di codice può essere testato su questo fantastico compiler explorer, a condizione che venga applicata la direttiva -O3 e che siamo sull'architettura x86.Comportamento GCC intransigente rispetto alla vettorizzazione e dimensione del loop
Qualcuno potrebbe spiegarmi la logica dietro le seguenti osservazioni?
#include <stdint.h>
void test(uint8_t* out, uint8_t const* in, uint32_t length)
{
unsigned const l1 = (length * 32)/32; // This is vectorized
unsigned const l2 = (length/32)*32; // This is not vectorized
unsigned const l3 = (length << 5)>>5; // This is vectorized
unsigned const l4 = (length >> 5)<<5; // This is not vectorized
unsigned const l5 = length -length%32; // This is not vectorized
unsigned const l6 = length & ~(32 -1); // This is not vectorized
for (unsigned i = 0; i<l1 /*pick your choice*/; ++i)
{
out[i] = in[i*2];
}
}
cosa mi lascia perplesso è che sia L1 ed L3 generare il codice vettorializzare nonostante non beeing garantito per essere multipli di 32. Tutte le altre lunghezze fanno non produrre codice vettorizzati, ma devono essere multipli di 32 C'è una ragione dietro questo?
Come parte, l'uso della direttiva #pragma omp simd in realtà non cambia nulla.
Edit: Dopo ulteriori indagini, la differenza di comportamento scompare quando il tipo di indice è size_t (e nessuna manipolazione di confine è ancora necessario), il che significa che questo genera codice vettorizzati:
#include <stdint.h>
#include <string>
void test(uint8_t* out, uint8_t const* in, size_t length)
{
for (size_t i = 0; i<length; ++i)
{
out[i] = in[i*2];
}
}
Se qualcuno sa perché la il ciclo vettoriale è così dipendente dal tipo di indice, quindi sarei curioso di saperne di più!
Edit2, grazie a Mark Lakata, O3 è effettivamente necessario
In quello che potrebbe essere visto come un'estensione di questa domanda, lo stesso identico comportamento è visibile con Clang, quindi suppongo che ci sia un po 'di logica in esso. –
Sembra che il compilatore teme che l'indice possa avvolgere e rinuncia per questo :-( –
Mi è stata spiegata la dipendenza dal tipo, legata al rischio di overflow (che impedisce la vettorizzazione). È consentito un overflow senza segno , mentre un overflow firmato non lo è, il che spiega quest'ultimo punto: l'utilizzo di un segno non firmato e il primo (eliminando efficacemente il rischio di overflow) consente la vettorizzazione, GCC è super intelligente: https://godbolt.org/g/SsVZ2r –