Visual Studio 2017: _mm_load_ps sering dikompilasi menjadi movup

Saya melihat rakitan yang dihasilkan untuk kode saya (menggunakan Visual Studio 2017) dan memperhatikan bahwa _mm_load_ps sering (selalu?) dikompilasi ke movup.

Data yang saya gunakan _mm_load_ps didefinisikan seperti ini:

struct alignas(16) Vector {
    float v[4];
}

// often embedded in other structs like this
struct AABB {
    Vector min;
    Vector max;
    bool intersection(/* parameters */) const;
}

Sekarang ketika saya menggunakan konstruksi ini, hal berikut akan terjadi:

// this code
__mm128 bb_min = _mm_load_ps(min.v);

// generates this
movups  xmm4, XMMWORD PTR [r8]

Saya mengharapkan movaps karena alignas(16). Apakah saya memerlukan sesuatu yang lain untuk meyakinkan kompiler agar menggunakan movaps dalam kasus ini?

EDIT: Pertanyaan saya berbeda dari pertanyaan ini karena saya tidak mengalami error apa pun. Strukturnya diselaraskan secara khusus dan saya juga menggunakan alokasi yang selaras. Sebaliknya, saya ingin tahu mengapa kompiler mengalihkan _mm_load_ps (intrinsik untuk memori yang selaras) ke movup. Jika saya tahu struct dialokasikan pada alamat yang selaras dan saya memanggilnya melalui ini* akan aman menggunakan movaps, bukan?


person B_old    schedule 09.03.2017    source sumber
comment
Untuk tujuan apa Anda secara khusus menginginkan movaps?   -  person harold    schedule 09.03.2017
comment
@harold Dia memindahkan empat pelampung dan instruksi yang selaras seringkali lebih berperforma, terutama pada beberapa generasi CPU.   -  person J...    schedule 09.03.2017
comment
Kemungkinan duplikat SSE, intrinsik, dan penyelarasan   -  person J...    schedule 09.03.2017
comment
@J... ya Core2. Sejauh yang saya tahu, tidak masalah pada apa pun yang lebih baru, selama alamatnya benar-benar selaras   -  person harold    schedule 09.03.2017
comment
tldr; alignas tidak sempurna atau jaminan, memcpy dapat meletakkan struct ini di mana saja (termasuk lokasi yang tidak selaras), malloc tidak akan selalu memberi Anda memori yang selaras, dll. Lihat penipuannya - biasanya Anda perlu menulis pengalokasi Anda sendiri menggunakan _aligned_malloc.   -  person J...    schedule 09.03.2017
comment
baca juga bagian Keterangan di sini. (Ini mengacu pada __declspec(align(#)), tetapi sejak dukungan VS2015 alignas diimplementasikan sebagai veneer untuk hal yang sama).   -  person J...    schedule 09.03.2017
comment
Diskusi di sini juga menarik .   -  person B_old    schedule 09.03.2017
comment
Secara definisi aman untuk menggunakan movaps untuk mengimplementasikan _mm_load_ps (terlepas dari penyelarasan sebenarnya), hal itu tampaknya tidak terjadi   -  person harold    schedule 09.03.2017
comment
@harold: Oke, tapi apakah itu sesuatu yang bisa saya pengaruhi? (Selain menulis kode assembler)   -  person B_old    schedule 09.03.2017
comment
Anda perlu memperlihatkan contoh lengkap yang menunjukkan masalah, termasuk opsi kompiler yang Anda gunakan dan versi Visual Studio 2017 yang Anda gunakan.   -  person Ross Ridge    schedule 09.03.2017
comment
@harold No movaps pasti akan menimbulkan pengecualian dengan alamat yang tidak selaras.   -  person J...    schedule 09.03.2017
comment
@J... ya dan _mm_load_ps diperbolehkan melakukan itu juga, meskipun tidak harus   -  person harold    schedule 09.03.2017
comment
Pada VS dan ICC, jika Anda mengkompilasi untuk AVX atau lebih tinggi, kompiler hampir tidak pernah mengeluarkan pemuatan/penyimpanan SIMD yang selaras. Itu diperbolehkan untuk melakukan itu karena tidak kehilangan fungsionalitas dan semua prosesor mulai dari Nehalem tidak memiliki penalti untuk menggunakan muatan/penyimpanan yang tidak selaras ketika alamatnya selaras. Mereka melakukannya karena membuat compiler menjadi lebih sederhana (tidak harus memilih antara selaras/tidak selaras) dan tidak crash jika tidak selaras. Meskipun saya sangat tidak setuju dengan yang terakhir karena saya lebih suka jika itu benar-benar crash karena ketidakselarasan karena itu adalah bug yang harus diperbaiki, bukan disembunyikan.   -  person Mysticial    schedule 10.03.2017
comment
@Mystical: Itu informasi bagus, tapi saya baru mengkompilasi untuk x64. Apakah hal yang sama berlaku di sana?   -  person B_old    schedule 10.03.2017
comment
@Mysticial Jawaban Anda terdengar cukup meyakinkan bagi saya. Mungkin mempostingnya sebagai jawaban sebenarnya jika Anda punya waktu   -  person Guillaume Gris    schedule 02.08.2017
comment
@GuillaumeGris Selesai.   -  person Mysticial    schedule 02.08.2017


Jawaban (1)


Pada versi terbaru Visual Studio dan Intel Compiler (terbaru pasca 2013?), kompiler jarang sekali lagi menghasilkan pemuatan/penyimpanan SIMD yang selaras.

Saat mengompilasi untuk AVX atau lebih tinggi:

  • Kompiler Microsoft (>VS2013?) tidak menghasilkan muatan yang selaras. Tapi itu masih menghasilkan toko yang selaras.
  • Kompiler Intel (> Parallel Studio 2012?) tidak berfungsi sama sekali lagi. Namun Anda masih akan melihatnya dalam biner yang dikompilasi ICC di dalam perpustakaannya yang dioptimalkan secara manual seperti memset().
  • Pada GCC 6.1, ini masih menghasilkan pemuatan/penyimpanan yang selaras saat Anda menggunakan intrinsik yang selaras.

Kompiler diperbolehkan melakukan ini karena tidak kehilangan fungsionalitas ketika kode ditulis dengan benar. Semua prosesor mulai dari Nehalem tidak memiliki penalti untuk pemuatan/penyimpanan yang tidak selaras ketika alamatnya selaras.

Pendirian Microsoft mengenai masalah ini adalah "membantu pemrogram agar tidak mogok". Sayangnya, saya tidak dapat lagi menemukan sumber asli pernyataan dari Microsoft ini. Menurut pendapat saya, hal ini menghasilkan kebalikannya karena menyembunyikan hukuman ketidakselarasan. Dari sudut pandang kebenaran, ini juga menyembunyikan kode yang salah.

Apa pun masalahnya, penggunaan pemuatan/penyimpanan yang tidak selaras tanpa syarat memang sedikit menyederhanakan kompiler.

Relevansi Baru:

  • Mulai Parallel Studio 2018, Intel Compiler tidak lagi menghasilkan gerakan yang selaras sama sekali - bahkan untuk target sebelum Nehalem.
  • Mulai dari Visual Studio 2017, Microsoft Compiler juga tidak lagi menghasilkan gerakan yang selaras sama sekali - bahkan ketika menargetkan perangkat keras pra-AVX.

Kedua kasus tersebut mengakibatkan penurunan kinerja yang tidak dapat dihindari pada prosesor lama. Namun tampaknya ini disengaja karena Intel dan Microsoft tidak lagi peduli dengan prosesor lama.


Satu-satunya pemuatan/penyimpanan intrinsik yang kebal terhadap hal ini adalah pemuatan/penyimpanan non-temporal. Tidak ada persamaan yang tidak selaras, jadi kompiler tidak punya pilihan.

Jadi jika Anda hanya ingin menguji kebenaran kode Anda, Anda dapat mengganti intrinsik pemuatan/penyimpanan dengan yang non-temporal. Namun berhati-hatilah untuk tidak membiarkan hal seperti ini masuk ke dalam kode produksi karena pemuatan/penyimpanan NT (khususnya penyimpanan NT) adalah pedang bermata dua yang dapat merugikan Anda jika Anda tidak tahu apa yang Anda lakukan.

person Mysticial    schedule 02.08.2017
comment
Terkait: gcc juga sangat menyukai penyelarasan saat melakukan vektorisasi otomatis, dan menggunakan skalar hingga batas penyelarasan (dengan kode intro/pembersihan yang terbuka sepenuhnya, yang merupakan banyak kode-bloat dengan AVX2 dan elemen kecil). Ia melakukan ini bahkan dengan -mtune=skylake atau sesuatu. Bagaimanapun, memastikan gcc mengetahui tentang jaminan penyelarasan apa pun yang dapat Anda berikan akan mengurangi penggembungan kode dan menghindari satu atau dua cabang bersyarat saat melakukan vektorisasi otomatis. - person Peter Cordes; 03.08.2017
comment
Beban NT pada memori tulis kembali berjalan persis sama dengan beban normal, setidaknya pada keluarga Intel Sandybridge. Mereka bisa membuatnya berfungsi seperti prefetchNTA, tetapi tidak melakukannya (mungkin karena memerlukan prefetcher perangkat keras yang sadar NT agar tidak menyedot). (Sedang mengerjakan pembaruan untuk stackoverflow.com/questions/32103968/; ternyata tebakan saya salah karena ia melakukan sesuatu seperti mengambil hanya satu cara cache untuk menghindari polusi. Hanya pfNTA yang melakukan itu .) - person Peter Cordes; 03.08.2017
comment
@PeterCordes Menariknya, throughput beban NT hanya 1/siklus di Skylake X dibandingkan dengan 2/siklus untuk semua beban lainnya. (menurut AIDA64) - person Mysticial; 03.08.2017
comment
Di Skylake-S (desktop), memuat ulang 64 byte yang sama dengan movntdqa xmm0, [rsi] / movntdqa xmm1, [rsi+16], dll. dijalankan ~1,71 per jam, vs. 2,0 per jam untuk movdqa. Jadi bahkan untuk kasus yang paling sepele pun, ini lebih lambat. Terima kasih telah menunjukkan hal itu. - person Peter Cordes; 03.08.2017
comment
Angka AIDA64 tersebut menunjukkan bahwa AVX512 EVEX vmovntdqa (1 per 1,08) berbeda dengan SSE biasa atau AVX VEX movntdqa (1 per 0,52). Dan EVEX VMOVNTDQA + VMOVNTDQ x/y/zmm reload/store masih memiliki latensi yang buruk, tetapi throughputnya adalah 1 per ~19,25c dan bukannya sama dengan latensi. (Dan latensi penyimpanan/muat ulang ZMM NT lebih rendah dibandingkan dua ukuran lainnya, yang merupakan petunjuk lain bahwa penyimpanan NT jalur cache penuh adalah spesial. Bandwidth single-thread yang jauh lebih tinggi dibandingkan penyimpanan NT yang lebih sempit sudah merupakan petunjuk besar.) - person Peter Cordes; 03.08.2017
comment
Ya. Saya belum mencoba mencari tahu apa yang berubah di bawahnya. Tetapi ketika saya mengaktifkan penyimpanan NT dalam kode saya, perbedaannya sangat drastis (sekitar 10 - 15%). Ini lebih dari apa yang saya lihat di Haswell. Memang benar, sebagian besar hal ini mungkin ada hubungannya dengan kemacetan bandwidth memori secara keseluruhan. - person Mysticial; 03.08.2017
comment
Mengenai penalti ketidaksejajaran penyembunyian: Hal ini juga terjadi pada clang/gcc dan AVX jika _mm_load[u]_ps dapat digabungkan dengan operasi lain (seperti vaddps): godbolt.org/z/2ZL5FQ Jadi, memaksa clang/gcc untuk benar-benar menghasilkan instruksi [v]movaps juga tidak selalu sepele. - person chtz; 15.05.2020