سؤال

أنا جديد على تحسين التعليمات البرمجية مع تعليمات SSE / SSE2 وحتى الآن لم أحصل على أبعد من ذلك. إلى حد علمي، ستبدو وظيفة شائعة محسنة SSE هذه:

void sse_func(const float* const ptr, int len){
    if( ptr is aligned )
    {
        for( ... ){
            // unroll loop by 4 or 2 elements
        }
        for( ....){
            // handle the rest
            // (non-optimized code)
        }
    } else {
        for( ....){
            // regular C code to handle non-aligned memory
        }
    }
}

ومع ذلك، كيف يمكنني تحديد ما إذا كانت الذاكرة بشكل صحيح ptr يشير إلى محاذاة من خلال 16 بايت؟ أعتقد أنه لا بد لي من تضمين مسار رمز C العادي لذاكرة عدم الانحياز حيث لا يمكنني التأكد من محاذاة كل ذاكرة مرت بهذه الوظيفة. واستخدامها في الواقع لتحميل البيانات من الذاكرة غير المعروفة في سجلات SSE تبدو بطيئة فظيعة (حتى أبطأ من رمز C العادي).

شكرا لكم مقدما...

هل كانت مفيدة؟

المحلول

تحرير: الصب ل long هي طريقة رخيصة لحماية نفسه من الأرجح إمكانية الملكية والمؤشرات هي أحجام مختلفة في الوقت الحاضر.

كما أشير في التعليقات أدناه، هناك حلول أفضل إذا كنت على استعداد لتضمين رأس ...

مؤشر p محاذاة على حدود 16 بايت IFF ((unsigned long)p & 15) == 0.

نصائح أخرى

#define is_aligned(POINTER, BYTE_COUNT) \
    (((uintptr_t)(const void *)(POINTER)) % (BYTE_COUNT) == 0)

يلقي إلى void * (أو، معافسة، char *) ضروري لأن المعيار يضمن فقط تحويل قابل للتحويل إلى uintptr_t بالنسبة void *.

إذا كنت تريد السلامة من النوع، ففكر في استخدام وظيفة مضمنة:

static inline _Bool is_aligned(const void *restrict pointer, size_t byte_count)
{ return (uintptr_t)pointer % byte_count == 0; }

والأمل في تحسين التحويل البرمجي إذا byte_count هو ثابت تجميع الوقت.

لماذا نحتاج إلى تحويل إلى void * ?

تتيح لغة C من تمثيلات مختلفة لأنواع المؤشر المختلفة، على سبيل المثال، يمكن أن يكون لديك 64 بت void * اكتب (مساحة العنوان بالكامل) و 32 بت foo * اكتب (شريحة).

التحويل foo * -> void * قد تنطوي على حساب فعلي، مثل إضافة إزاحة. يترك المعيار أيضا على التنفيذ ما يحدث عند تحويل المؤشرات (التعسفي) إلى الأعداد الصحيحة، لكنني أظن أنه غالبا ما يتم تنفيذه ك Noop.

لمثل هذا التنفيذ، foo * -> uintptr_t -> foo * سوف تعمل، ولكن foo * -> uintptr_t -> void * و void * -> uintptr_t -> foo * لن. لن يعمل حساب المحاذاة أيضا بشكل موثوق لأنك تحقق فقط محاذاة نسبة إلى إزاحة القطاع، والتي قد لا تكون أو لا تكون ما تريد.

في الختام: استخدم دائما void * للحصول على سلوك الاستقلال في التنفيذ.

تشير إجابات أخرى إلى مجموعة وتشغيلها مع مجموعة بت منخفضة ومقارنة مع الصفر.

لكن اختبار أكثر مباشرة إلى الأمام هو القيام بخطة مع قيمة المحاذاة المطلوبة، ومقارنة مع الصفر.

#define ALIGNMENT_VALUE     16u

if (((uintptr_t)ptr % ALIGNMENT_VALUE) == 0)
{
    // ptr is aligned
}

مع قالب وظيفة مثل

#include <type_traits>

template< typename T >
bool is_aligned(T* p){
    return !(reinterpret_cast<uintptr_t>(p) % std::alignment_of<T>::value);
}

يمكنك التحقق من المحاذاة في وقت التشغيل عن طريق استدعاء شيء مثل

struct foo_type{ int bar; }foo;
assert(is_aligned(&foo)); // passes

للتحقق من فشل المحاذاات السيئة، يمكنك القيام بذلك

// would almost certainly fail
assert(is_aligned((foo_type*)(1 + (uintptr_t)(&foo)));

هذا هو أساسا ما أستخدمه. من خلال إصدار عدد صحيح قالب، أكمل وقت تجميعه الموسع، لذلك لن ينتهي الأمر بتشغيل مودم بطيء مهما فعلت.

أنا دائما أحب التحقق من المدخلات الخاصة بي، لذلك وبالتالي تأكيد وقت الترجمة. إذا كانت قيمة المحاذاة خاطئة، فقد تجميعها جيدا ...

template <unsigned int alignment>
struct IsAligned
{
    static_assert((alignment & (alignment - 1)) == 0, "Alignment must be a power of 2");

    static inline bool Value(const void * ptr)
    {
        return (((uintptr_t)ptr) & (alignment - 1)) == 0;
    }
};

لمعرفة ما يحدث، يمكنك استخدام هذا:

// 1 of them is aligned...
int* ptr = new int[8];
for (int i = 0; i < 8; ++i)
    std::cout << IsAligned<32>::Value(ptr + i) << std::endl;

// Should give '1'
int* ptr2 = (int*)_aligned_malloc(32, 32);
std::cout << IsAligned<32>::Value(ptr2) << std::endl;

هل يمكنك فقط 'و' PTR مع 0x03 (محاذاة على 4S)، 0x07 (محاذاة على 8S) أو 0x0f (محاذاة على 16s) لمعرفة ما إذا كان يتم تعيين أي من أقل البتات؟

اترك ذلك للمهنيين،

https://www.boost.org/doc/libs/1_65_1/doc/html/align/reference.html#align.Reference.functions.is_aligned.

bool is_aligned(const void* ptr, std::size_t alignment) noexcept; 

مثال:

        char D[1];
        assert( boost::alignment::is_aligned(&D[0], alignof(double)) ); //  might fail, sometimes

ماذا عن:

void *mem = malloc(1024+15); 
void *ptr =( (*(char*)mem) - (*(char *)mem % 16) );
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top