জিসিসি কেন প্রায় একই সি কোডের জন্য এ জাতীয় মূলত বিভিন্ন সমাবেশ তৈরি করে?


184

একটি অনুকূলিত ftolফাংশন লেখার সময় আমি কিছু খুব বিজোড় আচরণ পেয়েছিGCC 4.6.1 । আমাকে আপনাকে প্রথমে কোডটি দেখান (স্পষ্টতার জন্য আমি পার্থক্য চিহ্নিত করেছি):

দ্রুত_আরঙ্ক_আপনি, সি:

int fast_trunc_one(int i) {
    int mantissa, exponent, sign, r;

    mantissa = (i & 0x07fffff) | 0x800000;
    exponent = 150 - ((i >> 23) & 0xff);
    sign = i & 0x80000000;

    if (exponent < 0) {
        r = mantissa << -exponent;                       /* diff */
    } else {
        r = mantissa >> exponent;                        /* diff */
    }

    return (r ^ -sign) + sign;                           /* diff */
}

দ্রুত_আরঙ্ক_দুই, সি:

int fast_trunc_two(int i) {
    int mantissa, exponent, sign, r;

    mantissa = (i & 0x07fffff) | 0x800000;
    exponent = 150 - ((i >> 23) & 0xff);
    sign = i & 0x80000000;

    if (exponent < 0) {
        r = (mantissa << -exponent) ^ -sign;             /* diff */
    } else {
        r = (mantissa >> exponent) ^ -sign;              /* diff */
    }

    return r + sign;                                     /* diff */
}

একই অধিকার মনে হচ্ছে? আচ্ছা জিসিসির দ্বিমত রয়েছে। সংকলনের পরেgcc -O3 -S -Wall -o test.s test.c অ্যাসেম্বলি আউটপুট:

দ্রুত_আরঙ্কিত_আর, উত্পন্ন:

_fast_trunc_one:
LFB0:
    .cfi_startproc
    movl    4(%esp), %eax
    movl    $150, %ecx
    movl    %eax, %edx
    andl    $8388607, %edx
    sarl    $23, %eax
    orl $8388608, %edx
    andl    $255, %eax
    subl    %eax, %ecx
    movl    %edx, %eax
    sarl    %cl, %eax
    testl   %ecx, %ecx
    js  L5
    rep
    ret
    .p2align 4,,7
L5:
    negl    %ecx
    movl    %edx, %eax
    sall    %cl, %eax
    ret
    .cfi_endproc

দ্রুত_আরঙ্ক_দুই, উত্পন্ন:

_fast_trunc_two:
LFB1:
    .cfi_startproc
    pushl   %ebx
    .cfi_def_cfa_offset 8
    .cfi_offset 3, -8
    movl    8(%esp), %eax
    movl    $150, %ecx
    movl    %eax, %ebx
    movl    %eax, %edx
    sarl    $23, %ebx
    andl    $8388607, %edx
    andl    $255, %ebx
    orl $8388608, %edx
    andl    $-2147483648, %eax
    subl    %ebx, %ecx
    js  L9
    sarl    %cl, %edx
    movl    %eax, %ecx
    negl    %ecx
    xorl    %ecx, %edx
    addl    %edx, %eax
    popl    %ebx
    .cfi_remember_state
    .cfi_def_cfa_offset 4
    .cfi_restore 3
    ret
    .p2align 4,,7
L9:
    .cfi_restore_state
    negl    %ecx
    sall    %cl, %edx
    movl    %eax, %ecx
    negl    %ecx
    xorl    %ecx, %edx
    addl    %edx, %eax
    popl    %ebx
    .cfi_restore 3
    .cfi_def_cfa_offset 4
    ret
    .cfi_endproc

এটি একটি চূড়ান্ত পার্থক্য। এটি আসলে খুব প্রোফাইলে প্রদর্শিত হয়, fast_trunc_one% প্রায় 30 চেয়ে দ্রুত fast_trunc_two। এখন আমার প্রশ্ন: এর কারণ কী?


1
পরীক্ষার উদ্দেশ্যে আমি এখানে একটি বাক্য তৈরি করেছি যেখানে আপনি সহজেই উত্সটি অনুলিপি / পেস্ট করতে পারেন এবং দেখতে পারেন যে আপনি জিসিসির অন্যান্য সিস্টেম / সংস্করণগুলিতে বাগটি পুনরুত্পাদন করতে পারেন।
orlp

12
পরীক্ষার কেসগুলি তাদের নিজস্ব ডিরেক্টরিতে রাখুন। তাদের সাথে সংকলন -S -O3 -da -fdump-tree-all। এটি মধ্যবর্তী প্রতিনিধিত্বের অনেক স্ন্যাপশট তৈরি করবে। পাশাপাশি তাদের (তারা সংখ্যাযুক্ত) পাশাপাশি চলুন এবং আপনি প্রথম ক্ষেত্রে অনুপস্থিত অপ্টিমাইজেশন খুঁজে পেতে সক্ষম হবেন।
zwol

1
পরামর্শ দুটি: সমস্ত পরিবর্তন intকরুন unsigned intএবং দেখুন পার্থক্যটি অদৃশ্য হয়ে যায় কিনা।
zwol

5
দুটি ফাংশন মনে হচ্ছে কিছুটা আলাদা গণিত করছে। ফলাফলগুলি একই হতে পারে তবে প্রকাশটি একই রকম (r + shifted) ^ signনয় r + (shifted ^ sign)। আমার ধারণা এটি অপ্টিমাইজারকে বিভ্রান্ত করছে? এফডব্লিউআইডাব্লিউ, এমএসভিসি 2010 (16.00.40219.01) এমন তালিকা তৈরি করে যা একে অপরের সাথে প্রায় সমান: gist.github.com/2430454
ডিসিডার

1
@ ডিডোডার: ওহ! আমি যে স্পট না। যদিও এটি পার্থক্যের জন্য ব্যাখ্যা নয়। আমি প্রশ্নটি এমন একটি নতুন সংস্করণ দিয়ে আপডেট করব যেখানে এটি অস্বীকার করা হয়েছে।
orlp

উত্তর:


256

ওপি'র সম্পাদনার সাথে সিঙ্ক করতে আপডেট হয়েছে

কোডটির সাথে টিঙ্কারিং করে, আমি জিসিসি প্রথম কেসটিকে কীভাবে অনুকূল করে ফেলে তা দেখতে পরিচালিত করেছি।

সেগুলি কেন এত আলাদা তা বোঝার আগে প্রথমে আমাদের অবশ্যই বুঝতে হবে কীভাবে জিসিসি অনুকূল হয় fast_trunc_one()

বিশ্বাস করুন বা না করুন, এটিতে fast_trunc_one()অনুকূলিত করা হচ্ছে:

int fast_trunc_one(int i) {
    int mantissa, exponent;

    mantissa = (i & 0x07fffff) | 0x800000;
    exponent = 150 - ((i >> 23) & 0xff);

    if (exponent < 0) {
        return (mantissa << -exponent);             /* diff */
    } else {
        return (mantissa >> exponent);              /* diff */
    }
}

এটি মূল হিসাবে ঠিক একই সমাবেশ উত্পন্ন করে fast_trunc_one()- নাম এবং সমস্ত কিছু নিবন্ধ করুন।

লক্ষ্য করুন যে xorসমাবেশে কোনও এস নেই fast_trunc_one()। এটাই আমার জন্য তা দিয়েছিল।


তা কিভাবে?


ধাপ 1: sign = -sign

প্রথমে signচলকটি একবার দেখুন । যেহেতু sign = i & 0x80000000;, কেবল দুটি সম্ভাব্য মান রয়েছে যা signনিতে পারে:

  • sign = 0
  • sign = 0x80000000

এখন উভয় ক্ষেত্রেই তা স্বীকৃতি দিন sign == -sign। সুতরাং, যখন আমি এখানে মূল কোডটি পরিবর্তন করি:

int fast_trunc_one(int i) {
    int mantissa, exponent, sign, r;

    mantissa = (i & 0x07fffff) | 0x800000;
    exponent = 150 - ((i >> 23) & 0xff);
    sign = i & 0x80000000;

    if (exponent < 0) {
        r = mantissa << -exponent;
    } else {
        r = mantissa >> exponent;
    }

    return (r ^ sign) + sign;
}

এটি আসল হিসাবে ঠিক একই সমাবেশ উত্পাদন করে fast_trunc_one()। আমি আপনাকে সমাবেশটি ছাড়িয়ে দেব, তবে এটি অভিন্ন - নাম এবং সমস্ত কিছু নিবন্ধ করুন।


পদক্ষেপ 2: গাণিতিক হ্রাস:x + (y ^ x) = y

signকেবল দুটি মানগুলির মধ্যে একটি নিতে পারে, 0বা 0x80000000

  • যখন x = 0, তারপর x + (y ^ x) = yতুচ্ছ হোল্ড।
  • যোগ করা এবং এর দ্বারা xore করা 0x80000000একই। এটি সাইন বিট ফ্লিপ। অতএব x + (y ^ x) = yএছাড়াও রাখা যখন x = 0x80000000

সুতরাং, x + (y ^ x)হ্রাস y। এবং কোডটি এটিকে সহজতর করে:

int fast_trunc_one(int i) {
    int mantissa, exponent, sign, r;

    mantissa = (i & 0x07fffff) | 0x800000;
    exponent = 150 - ((i >> 23) & 0xff);
    sign = i & 0x80000000;

    if (exponent < 0) {
        r = (mantissa << -exponent);
    } else {
        r = (mantissa >> exponent);
    }

    return r;
}

আবার এটি ঠিক একই সমাবেশে সংকলন করে - নাম এবং সমস্ত নিবন্ধ করুন।


এই উপরের সংস্করণটি শেষ পর্যন্ত এটি হ্রাস করে:

int fast_trunc_one(int i) {
    int mantissa, exponent;

    mantissa = (i & 0x07fffff) | 0x800000;
    exponent = 150 - ((i >> 23) & 0xff);

    if (exponent < 0) {
        return (mantissa << -exponent);             /* diff */
    } else {
        return (mantissa >> exponent);              /* diff */
    }
}

যা পুরোপুরি হ'ল জিসিসি সমাবেশে উত্পন্ন করে।


তাহলে কেন কম্পাইলার fast_trunc_two()একই জিনিসটির জন্য অনুকূলিত হয় না?

এর মূল অংশটি fast_trunc_one()হ'ল x + (y ^ x) = yঅপটিমাইজেশন। ইন অভিব্যক্তি শাখা জুড়ে বিভক্ত করা হচ্ছে।fast_trunc_two()x + (y ^ x)

আমি সন্দেহ করি যে এই অপটিমাইজেশনটি না করার জন্য জিসিসিকে বিভ্রান্ত করার পক্ষে যথেষ্ট। (এটি ^ -signশাখা থেকে উত্তোলন এবং r + signশেষের দিকে এটি মার্জ করা প্রয়োজন ।)

উদাহরণস্বরূপ, এটি একই সমাবেশ উত্পন্ন করে fast_trunc_one():

int fast_trunc_two(int i) {
    int mantissa, exponent, sign, r;

    mantissa = (i & 0x07fffff) | 0x800000;
    exponent = 150 - ((i >> 23) & 0xff);
    sign = i & 0x80000000;

    if (exponent < 0) {
        r = ((mantissa << -exponent) ^ -sign) + sign;             /* diff */
    } else {
        r = ((mantissa >> exponent) ^ -sign) + sign;              /* diff */
    }

    return r;                                     /* diff */
}

4
সম্পাদনা করুন, দেখে মনে হচ্ছে আমি দুটি সংশোধনীর উত্তর দিয়েছি। বর্তমান সংশোধনী দুটি উদাহরণকে উল্টিয়ে দিয়েছে এবং কোডটি কিছুটা পরিবর্তন করেছে ... এটি বিভ্রান্তিকর।
রহস্যময়

2
@ নাইটক্র্যাকার কোন উদ্বেগ নেই। আমি আমার উত্তরটি বর্তমান সংস্করণটির সাথে সিঙ্ক করতে আপডেট করেছি।
রহস্যময়

1
@ রহস্যময়ী: আপনার চূড়ান্ত বিবৃতিটি নতুন সংস্করণের সাথে আর সত্য নয়, আপনার উত্তরটি অকার্যকর করে তোলে (এটি সর্বাধিক গুরুত্বপূর্ণ প্রশ্নের উত্তর দেয় না, "জিসিসি কেন এই ধরণের আলাদা আলাদা সমাবেশ তৈরি করে" ।)
অরপাল

11
উত্তর আবার আপডেট হয়েছে। আমি নিশ্চিত না এটি যথেষ্ট সন্তুষ্ট কিনা। তবে আমি মনে করি না যে প্রাসঙ্গিক জিসিসি অপ্টিমাইজেশনের কাজ কীভাবে পাস হয় ঠিক তা না জেনে আমি আরও বেশি কিছু করতে পারি।
রহস্যময়

4
@ মিস্টিসিয়াল: কঠোরভাবে বলতে গেলে, যতক্ষণ না এই কোডটিতে স্বাক্ষরিত প্রকারটি ভুলভাবে ব্যবহৃত হচ্ছে, সংকলকটি এখানে যে রূপান্তরগুলি করছে তার বেশিরভাগ ক্ষেত্রেই আচরণটি সংজ্ঞায়িত এমন ক্ষেত্রে দেখা যায় ...
আর .. গিটিহাব স্টপ হেল্পিং আইসিসি

63

এটি সংকলকগুলির প্রকৃতি। ধরে নিই তারা দ্রুত বা সর্বোত্তম পথে নিবে তা বেশ মিথ্যা। যে কেউ সূচিত করে যে আপনার কোডটি অপ্টিমাইজ করার জন্য আপনার কিছু করার দরকার নেই কারণ "আধুনিক সংকলক" ফাঁকা পূরণ করে, সেরা কাজটি করে, দ্রুততম কোড তৈরি করে ইত্যাদি। আসলে আমি দেখেছি জিসিসি 3.x থেকে আরও খারাপ হতে শুরু করেছে কমপক্ষে বাহুতে 4.x। X.x এই পয়েন্টে caught.x পর্যন্ত ধরা পড়েছে, তবে এর প্রথম দিকে এটি ধীর কোড তৈরি করেছে। অনুশীলনের সাহায্যে আপনি কীভাবে আপনার কোডটি লিখবেন তা শিখতে পারেন যাতে সংকলকটিকে আরও কঠোর পরিশ্রম করতে হবে না এবং ফলস্বরূপ আরও সুসংগত এবং প্রত্যাশিত ফলাফল আসে।

এখানে বাগটি কী উত্পাদিত হবে তা আপনার প্রত্যাশা, আসলে যা উত্পাদিত হয়েছিল তা নয়। আপনি যদি কম্পাইলারটি একই আউটপুট উত্পন্ন করতে চান তবে এটি একই ইনপুটটি খাওয়ান। গাণিতিকভাবে একই নয়, একই রকম নয়, তবে বাস্তবে একইরকম, কোনও ভিন্ন পথ নেই, কোনও সংস্করণ থেকে অন্য সংস্করণে ভাগ করে নেওয়ার বা বিতরণ করার ব্যবস্থা নেই। আপনার কোডটি কীভাবে লিখবেন তা বোঝার এবং এটির সাথে সংকলকরা কী করে তা দেখার জন্য এটি একটি ভাল অনুশীলন। ধরে নেওয়ার ভুল করবেন না কারণ একদিন এক প্রসেসরের টার্গেটের জন্য জিসিসির একটি সংস্করণ একটি নির্দিষ্ট ফলাফল তৈরি করেছিল যা সমস্ত সংকলক এবং সমস্ত কোডের জন্য একটি নিয়ম। যা চলছে তার অনুভূতি পেতে আপনাকে অনেক সংকলক এবং অনেক লক্ষ্য ব্যবহার করতে হবে।

জিসিসি বেশ বাজে, আমি আপনাকে পর্দার পিছনে তাকানোর জন্য, জিসিসির সাহস দেখতে, কোনও লক্ষ্য যুক্ত করার চেষ্টা করুন বা নিজেকে কিছু সংশোধন করার জন্য আমন্ত্রণ জানাচ্ছি। এটি সবেমাত্র নল টেপ এবং জামিনের তারের দ্বারা একসাথে রাখা হয়। কোডের একটি অতিরিক্ত লাইন সংযুক্ত স্থানগুলিতে যুক্ত বা সরিয়ে নেওয়া হয়েছে এবং এটি নীচে ভেঙে পড়ে। এটি যেহেতু ব্যবহারযোগ্য কোড তৈরি করেছে তা হ'ল এটি কেন অন্যান্য প্রত্যাশা পূরণ করতে পারে তা নিয়ে চিন্তা করার পরিবর্তে সন্তুষ্ট হওয়ার মতো কিছু।

আপনি কী দেখেছিলেন জিসিসি-র বিভিন্ন সংস্করণ উত্পাদন করে? 3.x এবং 4.x বিশেষত 4.5 বনাম 4.6 বনাম 4.7 ইত্যাদি? এবং বিভিন্ন টার্গেট প্রসেসরের জন্য, x86, আর্ম, মিপস, ইত্যাদি বা x86 এর বিভিন্ন স্বাদগুলি যদি আপনি যে নেটিভ সংকলকটি ব্যবহার করেন, 32 বিট বনাম 64 বিট, ইত্যাদি? এবং তারপরে llvm (ঝনঝন) বিভিন্ন টার্গেটের জন্য?

মাইস্টিকাল কোড বিশ্লেষণ / অপ্টিমাইজ করার সমস্যার মধ্য দিয়ে কাজ করার জন্য প্রয়োজনীয় চিন্তাভাবনা প্রক্রিয়ায় একটি দুর্দান্ত কাজ করেছেন, প্রত্যাশিত যে কোনও "আধুনিক সংকলক" এর প্রত্যাশিত নয়, এর মধ্যে যে কোনও একটি সংকলক এসেছেন well

গণিতের বৈশিষ্ট্যগুলিতে না গিয়ে এই ফর্মের কোড

if (exponent < 0) {
  r = mantissa << -exponent;                       /* diff */
} else {
  r = mantissa >> exponent;                        /* diff */
}
return (r ^ -sign) + sign;                           /* diff */

সংকলককে A তে নেতৃত্ব দিতে চলেছে: এটিকে সেই ফর্মটিতে প্রয়োগ করুন, যদি-তবে-অন্যথায় সম্পাদন করুন তবে শেষ হয়ে ফিরে আসার জন্য সাধারণ কোডে রূপান্তর করুন। বা বি: এটি একটি ফাংশনটির লেজ শেষ হওয়ায় একটি শাখা সংরক্ষণ করুন। আর ব্যবহার করে বা সংরক্ষণ করেও বিরক্ত করবেন না।

if (exponent < 0) {
  return((mantissa << -exponent)^-sign)+sign;
} else {
  return((mantissa << -exponent)^-sign)+sign;
}

তারপরে আপনি মাইস্টিকাল হিসাবে চিহ্নিত হিসাবে সাইন ভেরিয়েবল কোড হিসাবে লিখিত হিসাবে একসাথে অদৃশ্য হয়ে যেতে পারে। আমি সংকলকটি সাইন ভেরিয়েবলটি চলে যেতে দেখবে বলে আশা করবো না তাই আপনার নিজের করা উচিত ছিল এবং সংকলকটিকে এটি বের করার চেষ্টা করতে বাধ্য করা হয়নি।

এটি জিসিসি সোর্স কোডটি খনন করার উপযুক্ত সুযোগ। দেখা যাচ্ছে আপনি এমন একটি মামলা খুঁজে পেয়েছেন যেখানে অপ্টিমাইজার এক ক্ষেত্রে একটি জিনিস দেখেছিল এবং অন্য ক্ষেত্রে অন্য জিনিস। তারপরে পরবর্তী পদক্ষেপ নিন এবং দেখুন যে কেসটি দেখতে আপনি জিসিসি পেতে পারেন না। প্রতিটি অপ্টিমাইজেশন রয়েছে কারণ কিছু ব্যক্তি বা গোষ্ঠী অপ্টিমাইজেশনকে স্বীকৃতি দিয়েছে এবং ইচ্ছাকৃতভাবে এটি সেখানে রেখেছিল। এই অপ্টিমাইজেশানটি সেখানে থাকার জন্য এবং প্রতিবার কাজ করার জন্য কাউকে সেখানে রেখে দিতে হবে (এবং তারপরে এটি পরীক্ষা করুন, এবং তারপরে এটি ভবিষ্যতে বজায় রাখবেন)।

নিশ্চয়ই ধরে নিবেন না যে কম কোডটি দ্রুত এবং আরও বেশি কোড ধীরে ধীরে, এটি সত্য না হওয়ার উদাহরণগুলি তৈরি এবং সন্ধান করা খুব সহজ। কম কোডের চেয়ে বেশি কোডের চেয়ে দ্রুত হওয়ার ক্ষেত্রে এটি প্রায়শই না ঘটে। আমি যেমন শুরু থেকে দেখিয়েছি যদিও আপনি সেই ক্ষেত্রে ব্রাঞ্চিং বা লুপিং ইত্যাদির জন্য আরও কোড তৈরি করতে পারেন এবং নেট ফলাফলটি দ্রুত কোড হতে পারে।

নীচের লাইনটি আপনি একটি সংকলককে আলাদা উত্স খাওয়ালেন এবং একই ফলাফল প্রত্যাশিত। সমস্যাটি সংকলক আউটপুট নয় বরং ব্যবহারকারীর প্রত্যাশা। কোনও নির্দিষ্ট সংকলক এবং প্রসেসরের জন্য এটি প্রদর্শন করা মোটামুটি সহজ, একটি লাইনের কোডের সংযোজন যা পুরো ফাংশনটিকে নাটকীয়ভাবে ধীর করে তোলে। উদাহরণস্বরূপ কেন একটি = বি + 2 পরিবর্তন করা হয়; to a = b + c + 2; কারণ _ফিল_ইন_এটি_ব্ল্যাঙ্ক_কম্পাইলার_নাম_তুল্যভাবে আলাদা এবং ধীর কোড তৈরি করে? সংকলক হওয়ার উত্তরটি অবশ্যই ইনপুটটিতে বিভিন্ন কোড খাওয়ানো হয়েছিল তাই বিভিন্ন আউটপুট উত্পন্ন করার জন্য কম্পাইলারের পক্ষে এটি পুরোপুরি বৈধ। (আরও ভাল হয় যখন আপনি কোডের দুটি সম্পর্কযুক্ত লাইন পরিবর্তন করেন এবং আউটপুটটিকে নাটকীয়ভাবে পরিবর্তনের কারণ ঘটাবেন) আউটপুটটির জটিলতা এবং আকারের সাথে ইনপুটটির জটিলতা এবং আকারের কোনও প্রত্যাশিত সম্পর্ক নেই।

for(ra=0;ra<20;ra++) dummy(ra);

এটি এসেম্বলারের 60-100 লাইনের মধ্যে কোথাও উত্পাদন করেছিল। এটি লুপটি অনিয়ন্ত্রিত। আমি লাইনগুলি গণনা করি নি, আপনি যদি এটির বিষয়ে চিন্তা করেন তবে এটি যুক্ত করতে হবে, ফাংশন কলে ইনপুটটিতে ফলাফলটি অনুলিপি করতে হবে, ফাংশন কল করতে হবে, ন্যূনতম তিনটি অপারেশন করতে হবে। সুতরাং লক্ষ্যমাত্রার উপর নির্ভর করে সম্ভবত 60 টি নির্দেশনা অন্ততপক্ষে, 80 প্রতি লুপে চারটি, 100 যদি লুপ প্রতি পাঁচটি, ইত্যাদি depending


কেন আপনার উত্তর ভাঙচুর করলেন? ওদেড সম্পাদনার সাথেও একমত বলে মনে হয়েছে ;-)।
পিটার - মনিকা পুনরায়

@ পিটারএ.স্নাইডার তার সমস্ত উত্তর একই তারিখে ভাঙচুর করা হয়েছে বলে মনে হয়। আমি মনে করি তার (চুরি?) অ্যাকাউন্টের ডেটা সহ কেউ এটি করেছেন।
ট্রিনিটি 420

23

মিস্টিয়াল ইতিমধ্যে একটি দুর্দান্ত ব্যাখ্যা দিয়েছে, তবে আমি ভেবেছিলাম যে আমি এফডাব্লুআইডাব্লু যুক্ত করব যে কোনও সংকলক কেন একজনের জন্য অপ্টিমাইজেশন তৈরি করবে এবং অন্যটির জন্য এটির পক্ষে মৌলিক কিছুই নেই।

clangউদাহরণস্বরূপ, এলএলভিএম এর সংকলক উভয় ফাংশনের জন্য একই কোড দেয় (ফাংশন নাম বাদে):

_fast_trunc_two:                        ## @fast_trunc_one
        movl    %edi, %edx
        andl    $-2147483648, %edx      ## imm = 0xFFFFFFFF80000000
        movl    %edi, %esi
        andl    $8388607, %esi          ## imm = 0x7FFFFF
        orl     $8388608, %esi          ## imm = 0x800000
        shrl    $23, %edi
        movzbl  %dil, %eax
        movl    $150, %ecx
        subl    %eax, %ecx
        js      LBB0_1
        shrl    %cl, %esi
        jmp     LBB0_3
LBB0_1:                                 ## %if.then
        negl    %ecx
        shll    %cl, %esi
LBB0_3:                                 ## %if.end
        movl    %edx, %eax
        negl    %eax
        xorl    %esi, %eax
        addl    %edx, %eax
        ret

এই কোডটি ওপির প্রথম জিসিসি সংস্করণ হিসাবে সংক্ষিপ্ত নয়, তবে দ্বিতীয়টির মতো দীর্ঘ নয়।

X86_64 এর জন্য সংকলন করে অন্য সংকলকটির কোড (যা আমি নাম দেব না) উভয় ফাংশনের জন্য এটি উত্পাদন করে:

fast_trunc_one:
        movl      %edi, %ecx        
        shrl      $23, %ecx         
        movl      %edi, %eax        
        movzbl    %cl, %edx         
        andl      $8388607, %eax    
        negl      %edx              
        orl       $8388608, %eax    
        addl      $150, %edx        
        movl      %eax, %esi        
        movl      %edx, %ecx        
        andl      $-2147483648, %edi
        negl      %ecx              
        movl      %edi, %r8d        
        shll      %cl, %esi         
        negl      %r8d              
        movl      %edx, %ecx        
        shrl      %cl, %eax         
        testl     %edx, %edx        
        cmovl     %esi, %eax        
        xorl      %r8d, %eax        
        addl      %edi, %eax        
        ret                         

এটি আকর্ষণীয় যে এটি উভয় পক্ষের গণনা করে if এবং তারপরে বেছে নিতে শেষে শর্তযুক্ত পদক্ষেপ ব্যবহার করে।

মুক্ত 64 সংকলক নিম্নলিখিত উত্পাদন করে:

fast_trunc_one: 
    movl %edi,%r9d                  
    sarl $23,%r9d                   
    movzbl %r9b,%r9d                
    addl $-150,%r9d                 
    movl %edi,%eax                  
    movl %r9d,%r8d                  
    andl $8388607,%eax              
    negl %r8d                       
    orl $8388608,%eax               
    testl %r8d,%r8d                 
    jl .LBB2_fast_trunc_one         
    movl %r8d,%ecx                  
    movl %eax,%edx                  
    sarl %cl,%edx                   
.Lt_0_1538:
    andl $-2147483648,%edi          
    movl %edi,%eax                  
    negl %eax                       
    xorl %edx,%eax                  
    addl %edi,%eax                  
    ret                             
    .p2align 5,,31
.LBB2_fast_trunc_one:
    movl %r9d,%ecx                  
    movl %eax,%edx                  
    shll %cl,%edx                   
    jmp .Lt_0_1538                  

এবং অনুরূপ, তবে অভিন্ন নয়, এর জন্য কোড fast_trunc_two

যাইহোক, যখন এটি অপ্টিমাইজেশনের আসে, এটি একটি লটারি হয় - এটি হ'ল ... আপনার কোড কেন কোনও নির্দিষ্ট উপায়ে সংকলিত হয় তা জানা সর্বদা সহজ নয়।


10
সংকলকটি কি আপনি কোনও শীর্ষ-গোপন সুপার কম্পিউটারের নাম রাখবেন না?
orlp

4
শীর্ষ সিক্রেট সংকলক সম্ভবত ইন্টেল icc। আমার কাছে কেবল 32-বিট ভেরিয়েন্ট রয়েছে তবে এটি এর সাথে খুব সাদৃশ্য কোড তৈরি করে।
জানুস ট্রয়েলসন 21

5
আমি বিশ্বাস করি এটি আইসিসি। সংকলক জানে যে প্রসেসর নির্দেশ স্তরের সমান্তরালতা সক্ষম এবং এইভাবে উভয় শাখা একসাথে গণনা করা যেতে পারে। শর্তাধীন পদক্ষেপের ওভারহেড মিথ্যা শাখার পূর্বাভাসের ওভারহেডের তুলনায় অনেক কম।
ফিলিপ নাভারা
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.