كيف يمكن لي أن تنفيذ التضمين المنطقي مع أحادي المعامل أو رمز كفاءة آخرين في C؟

StackOverflow https://stackoverflow.com/questions/668653

سؤال

وأريد لتنفيذ العملية المنطقية التي تعمل كفاءة قدر الإمكان. أحتاج هذا الجدول الحقيقة:

p    q    p → q
T    T      T
T    F      F
F    T      T
F    F      T

وهذا، وفقا لويكيبيديا يسمى " منطقية ضمنا "

ولقد كنت تحاول منذ فترة طويلة لمعرفة كيفية جعل هذا مع عمليات المختصة بالبت في C دون استخدام الشرطية. ربما شخص ما قد حصلت على بعض الأفكار حول هذا الموضوع.

والشكر

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

المحلول

ولمعلوماتك، مع دول مجلس التعاون الخليجي-4.3.3:

int foo(int a, int b) { return !a || b; }
int bar(int a, int b) { return ~a | b; }

ويعطي (من -d objdump):

0000000000000000 <foo>:
   0:   85 ff                   test   %edi,%edi
   2:   0f 94 c2                sete   %dl
   5:   85 f6                   test   %esi,%esi
   7:   0f 95 c0                setne  %al
   a:   09 d0                   or     %edx,%eax
   c:   83 e0 01                and    $0x1,%eax
   f:   c3                      retq   

0000000000000010 <bar>:
  10:   f7 d7                   not    %edi
  12:   09 fe                   or     %edi,%esi
  14:   89 f0                   mov    %esi,%eax
  16:   c3                      retq   

وهكذا، لا الفروع، ولكن ضعف عدد التعليمات.

وحتى أفضل، مع _Bool (بفضلlitb):

_Bool baz(_Bool a, _Bool b) { return !a || b; }
0000000000000020 <baz>:
  20:   40 84 ff                test   %dil,%dil
  23:   b8 01 00 00 00          mov    $0x1,%eax
  28:   0f 45 c6                cmovne %esi,%eax
  2b:   c3                      retq   

وهكذا، وذلك باستخدام _Bool بدلا من int هو فكرة جيدة.

وبما أنني استكمال اليوم، لقد أكدت دول مجلس التعاون الخليجي 8.2.0 ينتج مماثلة، وإن لم تكن متطابقة، نتائج _Bool:

0000000000000020 <baz>:
  20:   83 f7 01                xor    $0x1,%edi
  23:   89 f8                   mov    %edi,%eax
  25:   09 f0                   or     %esi,%eax
  27:   c3                      retq   

نصائح أخرى

~p | q

لالتصور:

perl -e'printf "%x\n", (~0x1100 | 0x1010) & 0x1111'
1011

في كود ضيق، وهذا ينبغي أن يكون أسرع من "! ص || ف" لأن هذا الأخير لديه فرع، والتي قد تتسبب في كشك في وحدة المعالجة المركزية بسبب خطأ التنبؤ فرع. النسخة المختصة بالبت غير القطعية و، على سبيل المكافأة، ويمكن القيام 32 مرات كما الكثير من العمل في عدد صحيح 32-بت من الإصدار منطقية!

!p || q

وغير الكثير بسرعة. على محمل الجد، لا تقلق بشأن ذلك.

ويمكنك تقرأ على اشتقاق التعبيرات المنطقية من جداول الحقيقة (وأيضا يمكنك الاطلاع على شكل الكنسي )، على الطريقة التي يمكن أن تعبر عن أي جدول الحقيقة كما مزيج من البدائيون منطقية أو وظائف.

وهناك حل آخر لالقيم المنطقية C (قليلا قذرة، ولكن الأعمال):

و((unsigned int)(p) <= (unsigned int)(q))

وكان يعمل منذ وفق المعايير C، 0 يمثل كاذبة، وأية قيمة أخرى حقيقية (يتم إرجاع 1 للصحيح من قبل مشغلي منطقية، نوع int).

وو"قذارة" هي التي تستخدم القيم المنطقية (p وq) أنها أعداد صحيحة، وهو ما يتناقض بعض السياسات الكتابة قوية (مثل ميسرا)، حسنا، هذا هو السؤال الأمثل. تستطيع #define دائما بانها الماكرو لإخفاء الأشياء القذرة.

لp منطقية سليمة وq (وجود إما 0 أو تمثيلات الثنائية 1) يعمل. وإلا قد تفشل T->T لإنتاج T إذا p وq لها قيمة غير صفرية التعسفي لتمثيل صحيح.

إذا كنت بحاجة إلى تخزين النتيجة فقط، لأن بنتيوم II، هناك (نقل الشرطي) تعليمات cmovcc (كما هو مبين في الجواب Derobert ل). لالقيم المنطقية، ولكن حتى 386 كان الخيار فروع، تعليمات setcc، التي تنتج 0 أو 1 في موقع نتيجة بايت (السجل بايت أو الذاكرة). يمكنك أن ترى أيضا أنه في الجواب Derobert، وويجمع هذا الحل أيضا إلى نتيجة تنطوي على setcc (setbe: تعيين إذا كان أقل من أو يساوي).

وDerobert وكريس دولان في ~p | q البديل يجب أن يكون الأسرع لمعالجة كميات كبيرة من البيانات لأنه يمكن معالجة الآثار المترتبة على كل بت من p وq على حدة.

لاحظ أنه حتى الحل !p || q يجمع إلى المتفرعة متاحة على إلى x86: أنه يستخدم تعليمات setcc. هذا هو الحل الأمثل إذا p أو q قد تحتوي على قيم غير صفرية التعسفية التي تمثل صحيح. إذا كنت تستخدم نوع _Bool، فإنه سيتم إنشاء عدد قليل جدا من الإرشادات.

وحصلت على الأرقام التالية عند تجميع لإلى x86:

__attribute__((fastcall)) int imp1(int a, int b)
{
 return ((unsigned int)(a) <= (unsigned int)(b));
}

__attribute__((fastcall)) int imp2(int a, int b)
{
 return (!a || b);
}

__attribute__((fastcall)) _Bool imp3(_Bool a, _Bool b)
{
 return (!a || b);
}

__attribute__((fastcall)) int imp4(int a, int b)
{
 return (~a | b);
}

ونتيجة الجمعية:

00000000 <imp1>:
   0:   31 c0                   xor    %eax,%eax
   2:   39 d1                   cmp    %edx,%ecx
   4:   0f 96 c0                setbe  %al
   7:   c3                      ret    

00000010 <imp2>:
  10:   85 d2                   test   %edx,%edx
  12:   0f 95 c0                setne  %al
  15:   85 c9                   test   %ecx,%ecx
  17:   0f 94 c2                sete   %dl
  1a:   09 d0                   or     %edx,%eax
  1c:   0f b6 c0                movzbl %al,%eax
  1f:   c3                      ret    

00000020 <imp3>:
  20:   89 c8                   mov    %ecx,%eax
  22:   83 f0 01                xor    $0x1,%eax
  25:   09 d0                   or     %edx,%eax
  27:   c3                      ret    

00000030 <imp4>:
  30:   89 d0                   mov    %edx,%eax
  32:   f7 d1                   not    %ecx
  34:   09 c8                   or     %ecx,%eax
  36:   c3                      ret    

عند استخدام نوع _Bool، مترجم يستغل بوضوح أن فقد اثنين من القيم الممكنة فقط (0 لكاذبة و1 لصحيح)، مما ينتج عنه نتيجة مشابهة جدا لحل ~a | b (الفرق الوحيد هو أن هذا الأخير يقوم مكمل على كل بت بدلا من مجرد أدنى بت).

وتجميع 64 بت يعطي تقريبا نفس النتائج.

وعلى أي حال، فمن الواضح، وطريقة لا يهم حقا من وجهة تجنب الشرطية المنتجة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top