2012-11-06 33 views
14

Tôi đang cố gắng tìm hiểu để mã sử dụng intrinsics và dưới đây là một mã mà không bổ sungin một biến __m128i

compiler used: icc

#include<stdio.h> 
#include<emmintrin.h> 
int main() 
{ 
     __m128i a = _mm_set_epi32(1,2,3,4); 
     __m128i b = _mm_set_epi32(1,2,3,4); 
     __m128i c; 
     c = _mm_add_epi32(a,b); 
     printf("%d\n",c[2]); 
     return 0; 
} 

tôi nhận được báo lỗi dưới đây:

test.c(9): error: expression must have pointer-to-object type 
     printf("%d\n",c[2]); 

Làm cách nào để in các giá trị trong biến số c thuộc loại __m128i

+0

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

+1

liên quan đến tiêu đề: [cách in __uint128_t số bằng gcc?] (Http://stackoverflow.com/q/11656241/4279) – jfs

+1

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. –

Trả lời

16

Sử dụng chức năng này để in chúng:

void print128_num(__m128i var) 
{ 
    uint16_t *val = (uint16_t*) &var; 
    printf("Numerical: %i %i %i %i %i %i %i %i \n", 
      val[0], val[1], val[2], val[3], val[4], val[5], 
      val[6], val[7]); 
} 

Bạn chia 128 bit thành 16 bit (hoặc 32 bit) trước khi in.

Đây là một cách để tách 64-bit và in ấn nếu bạn có hỗ trợ 64-bit có sẵn:

void print128_num(__m128i var) 
{ 
    int64_t *v64val = (int64_t*) &var; 
    printf("%.16llx %.16llx\n", v64val[1], v64val[0]); 
} 
+0

Thay 'llx' bằng' lld' nếu u muốn int. – askmish

+0

hoạt động. Tôi đã sử dụng uint32_t để in các số nguyên 32 bit. Nhưng đầu ra được đảo ngược. Thay vì '2,4,6,8' tôi nhận được' 8,6,4,2'. Có '_mm_add_epi32' lưu trữ các giá trị theo thứ tự ngược lại không? – arunmoezhi

+0

Bạn đã đọc Endian-ness chưa? – askmish

1
#include<stdio.h> 
#include<emmintrin.h> 
int main() 
{ 
    __m128i a = _mm_set_epi32(1,2,3,4); 
    __m128i b = _mm_set_epi32(1,2,3,4); 
    __m128i c; 

    const int32_t* q; 
    //add a pointer 
    c = _mm_add_epi32(a,b); 

    q = (const int32_t*) &c; 
    printf("%d\n",q[2]); 
    //printf("%d\n",c[2]); 
    return 0; 
} 

Hãy thử mã này.

+3

Mã bí danh này là con trỏ. Bạn có chắc nó là hợp pháp không? –

+0

@NateEldredge: Tôi chắc chắn điều này là * không * nghiêm chỉnh pháp lý (trừ khi bạn sử dụng '-fno-strict-aliasing' hoặc một cái gì đó). Tôi đã đăng một câu trả lời an toàn. –

4

Tôi biết câu hỏi này được gắn thẻ C, nhưng đó cũng là kết quả tìm kiếm tốt nhất khi tìm kiếm giải pháp C++ cho cùng một vấn đề.

Vì vậy, đây có thể là C++ thực hiện:

#include <string> 
#include <cstring> 
#include <sstream> 

#if defined(__SSE2__) 
template <typename T> 
std::string __m128i_toString(const __m128i var) { 
    std::stringstream sstr; 
    T values[16/sizeof(T)]; 
    std::memcpy(values,&var,sizeof(values)); //See discussion below 
    if (sizeof(T) == 1) { 
     for (unsigned int i = 0; i < sizeof(__m128i); i++) { //C++11: Range for also possible 
      sstr << (int) values[i] << " "; 
     } 
    } else { 
     for (unsigned int i = 0; i < sizeof(__m128i)/sizeof(T); i++) { //C++11: Range for also possible 
      sstr << values[i] << " "; 
     } 
    } 
    return sstr.str(); 
} 
#endif 

Cách sử dụng:

#include <iostream> 
[..] 
__m128i x 
[..] 
std::cout << __m128i_toString<uint8_t>(x) << std::endl; 
std::cout << __m128i_toString<uint16_t>(x) << std::endl; 
std::cout << __m128i_toString<uint32_t>(x) << std::endl; 
std::cout << __m128i_toString<uint64_t>(x) << std::endl; 

Kết quả:

141 114 0 0 0 0 0 0 151 104 0 0 0 0 0 0 
29325 0 0 0 26775 0 0 0 
29325 0 26775 0 
29325 26775 

Lưu ý: có tồn tại một cách đơn giản để tránh những if (size(T)==1), xem https://stackoverflow.com/a/28414758/2436175

+0

Bạn nên sử dụng 'alignas (16) T giá trị [16/sizeof (T)];' và '_mm_storeu_si128 ((__m128i *) giá trị, var);' Tất cả phần còn lại của mã hoạt động tốt sau đó. Và đơn giản hóa, bởi vì bạn có thể sử dụng một phạm vi-cho như 'for (T v: values)', tôi nghĩ vậy. –

+0

@PeterCordes Tôi thấy quan điểm của bạn. Tôi tự hỏi nếu người ta có thể chỉ đơn giản là sử dụng một memcpy thay vào đó, mà sẽ phụ tùng sự cần thiết của yêu cầu một bộ đệm liên kết. – Antonio

+0

Xem câu trả lời của tôi.Sử dụng 'storeu' thay vì' store' nếu bạn không có C++ 11 cho 'alignas', hoặc chỉ thị trình biên dịch cụ thể. Nó có lẽ vẫn sẽ tối ưu hóa đi. (Và BTW, Windows/Linux hiện đại đã sắp xếp ngăn xếp theo 16B, do đó, nó không tốn bất kỳ thứ gì của trình biên dịch để căn chỉnh bộ đệm nếu nó thực sự lưu trữ/tải lại.) –

4
  • 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).