- Di động qua gcc/clang/ICC/MSVC, C và C++.
- hoàn toàn an toàn với tất cả các cấp tối ưu hóa: không có con trỏ aliasing (không giống như hầu hết các câu trả lời khác)
- in trong hex như u8, U16, u32, hoặc các yếu tố u64 (based on @AG1's answer)
- Prints để nhớ (yếu tố ít quan trọng nhất đầu tiên, như
_mm_setr_epiX
). Đảo ngược các chỉ số mảng nếu bạn thích in theo cùng thứ tự các sách hướng dẫn sử dụng của Intel, trong đó phần tử quan trọng nhất nằm ở bên trái (như _mm_set_epiX
). Liên quan: Convention for displaying vector registers
Sử dụng một __m128i*
để tải từ một mảng của int
là an toàn vì __m128
loại được định nghĩa để cho phép răng cưa. (ví dụ: trong tiêu đề của gcc, định nghĩa bao gồm __attribute__((may_alias))
.)
Đảo ngược không phải là an toàn. Nó có thể xảy ra để làm việc trong hầu hết các trường hợp, nhưng tại sao lại có rủi ro?
(uint32_t*) &my_vector
vi phạm quy tắc đặt bí danh C và C++ và không được bảo đảm hoạt động theo cách bạn mong đợi. Lưu trữ vào một mảng địa phương và sau đó truy cập nó được đảm bảo an toàn. Nó thậm chí còn tối ưu hóa với hầu hết các trình biên dịch, vì vậy bạn có thể mua movq
/pextrq
trực tiếp từ sổ đăng ký số nguyên xy đến số nguyên thay vì một ví dụ thực tế lưu trữ/tải lại.
Source + asm output on the Godbolt compiler explorer: bằng chứng nó biên dịch với MSVC v.v.
#include <immintrin.h>
#include <stdint.h>
#include <stdio.h>
#ifndef __cplusplus
#include <stdalign.h> // C11 defines _Alignas(). This header defines alignas()
#endif
void p128_hex_u8(__m128i in) {
alignas(16) uint8_t v[16];
_mm_store_si128((__m128i*)v, in);
printf("v16_u8: %x %x %x %x | %x %x %x %x | %x %x %x %x | %x %x %x %x\n",
v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7],
v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15]);
}
void p128_hex_u16(__m128i in) {
alignas(16) uint16_t v[8];
_mm_store_si128((__m128i*)v, in);
printf("v8_u16: %x %x %x %x, %x %x %x %x\n", v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
}
void p128_hex_u32(__m128i in) {
alignas(16) uint32_t v[4];
_mm_store_si128((__m128i*)v, in);
printf("v4_u32: %x %x %x %x\n", v[0], v[1], v[2], v[3]);
}
void p128_hex_u64(__m128i in) {
alignas(16) long long v[2]; // uint64_t might give format-string warnings with %llx; it's just long in some ABIs
_mm_store_si128((__m128i*)v, in);
printf("v2_u64: %llx %llx\n", v[0], v[1]);
}
Nếu bạn cần di động để C99 hoặc C++ 03 hoặc sớm hơn (nghĩa là không phải C11/C++ 11), loại bỏ các alignas()
và sử dụng storeu
thay vì store
. Hoặc sử dụng __attribute__((aligned(16)))
hoặc __declspec(align(16))
để thay thế.
(Nếu bạn đang viết mã với nội tại, bạn nên sử dụng phiên bản trình biên dịch gần đây. Các trình biên dịch mới hơn thường làm tốt hơn các trình biên dịch cũ hơn, bao gồm cả nội tại SSE/AVX. Có thể bạn muốn sử dụng gcc-6.3 -std=gnu++03
C++ 03 chế độ cho một codebase đó không phải là sẵn sàng cho C++ 11 hoặc một cái gì đó.)
đầu ra mẫu từ kêu gọi tất cả 4 chức năng trên
// source used:
__m128i vec = _mm_setr_epi8(1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15, 16);
// output:
v2_u64: 0x807060504030201 0x100f0e0d0c0b0a09
v4_u32: 0x4030201 0x8070605 0xc0b0a09 0x100f0e0d
v8_u16: 0x201 0x403 0x605 0x807 | 0xa09 0xc0b 0xe0d 0x100f
v16_u8: 0x1 0x2 0x3 0x4 | 0x5 0x6 0x7 0x8 | 0x9 0xa 0xb 0xc | 0xd 0xe 0xf 0x10
điều chỉnh chuỗi định dạng nếu bạn muốn pad với số không đứng đầu cho phù hợp ou chiều rộng tput. Xem printf(3)
.
Cũng lưu ý rằng '__m128i' không có bất kỳ thông tin nào về loại đang được lưu trữ. Nó có thể là 8-bit ints, int-bit 16 bit, 32 bit, vv ... Một số trình biên dịch hỗ trợ phần mở rộng trường '.m128i_i32'. Nhưng nó chắc chắn không phải là tiêu chuẩn và không phải trong GCC. – Mysticial
liên quan đến tiêu đề: [cách in __uint128_t số bằng gcc?] (Http://stackoverflow.com/q/11656241/4279) – jfs
Lưu ý rằng một số trình biên dịch có hỗ trợ printf tích hợp cho các loại SIMD, ví dụ: Các phiên bản gcc, clang của Apple, tất cả đều hỗ trợ '% vld' để in một' __m128i' thành int x 4 bit 32 bit. –