জিসিসি কেন ধরে নিতে পারে না যে এই লুপটিতে স্ট্যান্ড :: ভেক্টর :: আকার পরিবর্তন হবে না?


14

আমি একজন সহকর্মীকে দাবী করেছি যে if (i < input.size() - 1) print(0);এই লুপটিতে অনুকূলিত হবে যাতে input.size()প্রতিটি পুনরাবৃত্তিতে এটি না পড়ে, তবে দেখা যায় যে এটি ঘটেনি!

void print(int x) {
    std::cout << x << std::endl;
}

void print_list(const std::vector<int>& input) {
    int i = 0;
    for (size_t i = 0; i < input.size(); i++) {
        print(input[i]);
        if (i < input.size() - 1) print(0);
    }
}

জিসিসি বিকল্পগুলির সাথে সংকলক এক্সপ্লোরার অনুযায়ী -O3 -fno-exceptionsআমরা প্রকৃতপক্ষে input.size()প্রতিটি পুনরাবৃত্তি পড়ছি এবং leaএকটি বিয়োগফল সম্পাদন করতে ব্যবহার করছি!

        movq    0(%rbp), %rdx
        movq    8(%rbp), %rax
        subq    %rdx, %rax
        sarq    $2, %rax
        leaq    -1(%rax), %rcx
        cmpq    %rbx, %rcx
        ja      .L35
        addq    $1, %rbx

মজার বিষয় হচ্ছে মরিচায় এই অপটিমাইজেশন ঘটে। দেখে মনে হচ্ছে যে প্রতিটি iপরিবর্তনকে jহ্রাস করা যায় এমন একটি ভেরিয়েবলের সাথে প্রতিস্থাপিত হয় এবং পরীক্ষার i < input.size() - 1মতো কিছু পরিবর্তিত হয় j > 0

fn print(x: i32) {
    println!("{}", x);
}

pub fn print_list(xs: &Vec<i32>) {
    for (i, x) in xs.iter().enumerate() {
        print(*x);
        if i < xs.len() - 1 {
            print(0);
        }
    }
}

ইন কম্পাইলার এক্সপ্লোরার প্রাসঙ্গিক সমাবেশ ভালো দেখায়:

        cmpq    %r12, %rbx
        jae     .LBB0_4

আমি চেক করা এবং আমি নিশ্চিত r12হয় xs.len() - 1এবং rbxপাল্টা হয়। এর আগে সেখানে একটি হল addজন্য rbxএবং একটি movমধ্যে লুপ বাইরে r12

কেন? দেখে মনে হচ্ছে যে, জিসিসি যদি ইনলাইন করতে সক্ষম হয় size()এবং operator[]যেমনটি করেছে, এটি জানতে সক্ষম হওয়া উচিত যে size()এটি পরিবর্তন হয় না। তবে হতে পারে জিসিসির অপ্টিমাইজার বিচারকরা এটিকে ভেরিয়েবলের মধ্যে টানিয়ে দেওয়ার মতো নয়? অথবা হতে পারে এমন আরও কিছু সম্ভাব্য পার্শ্ব প্রতিক্রিয়া যা এটি অনিরাপদ করে তুলবে - কেউ কি জানেন?


1
এছাড়াও printlnসম্ভবত একটি জটিল পদ্ধতি, printlnসংকলকটিতে এটি প্রমাণ করতে সমস্যা হতে পারে যা ভেক্টরকে রূপান্তরিত করে না।
হাঁসকে

1
@ মুভিংডাক: আর একটি থ্রেড হবে ডাটা-রেস ইউবি। কম্পাইলার করতে পারেন এবং অনুমান করি যে ঘটবে না। এখানে সমস্যা হ'ল নন-ইনলাইন ফাংশন কল cout.operator<<()। সংকলক জানে না যে এই ব্ল্যাক-বক্স ফাংশনটি কোনও std::vectorগ্লোবাল থেকে কোনও রেফারেন্স পায় না ।
পিটার কর্ডেস

@ পিটারকর্ডস: আপনি ঠিক বলেছেন যে অন্যান্য থ্রেডগুলি একক ব্যাখ্যা নয়, এবং এর জটিলতা printlnবা operator<<মূল কী।
মাকিং হাঁস

সংকলক এই বাহ্যিক পদ্ধতির শব্দার্থকতা জানেন না।
ব্যবহারকারী 207421

উত্তর:


10

নন-ইনলাইন ফাংশন কলটি cout.operator<<(int)অপ্টিমাইজারের জন্য একটি কালো বাক্স (কারণ গ্রন্থাগারটি কেবলমাত্র সি ++ তে লেখা এবং সমস্ত অপ্টিমাইজার দেখায় একটি প্রোটোটাইপ; মন্তব্যে আলোচনা দেখুন)। এটি এমন কোনও মেমরি ধরে নিতে হবে যা সম্ভবত কোনও বৈশ্বিক ভার দ্বারা চিহ্নিত করা যেতে পারে mod

(বা std::endlকল। বিটিডাব্লু, কেন কেবলমাত্র একটি মুদ্রণের পরিবর্তে সেই পর্যায়ে চলাচলের চাপকে চাপ দিন '\n'?)

উদাহরণস্বরূপ , এটি সমস্তstd::vector<int> &input কিছুর জন্য জানে, এটি একটি বৈশ্বিক চলক সম্পর্কিত একটি রেফারেন্স এবং সেই ফাংশন কলগুলির মধ্যে একটি হ'ল সেই বৈশ্বিক পরিবর্তকে পরিবর্তন করে । (বা কোনও গ্লোবাল vector<int> *ptrকোথাও আছে, বা এমন একটি ফাংশন রয়েছে যা static vector<int>অন্য কোনও সংকলন ইউনিটের কোনও পয়েন্টারকে ফেরত দেয় , বা অন্য কোনও উপায়ে যে কোনও ফাংশন আমাদের দ্বারা এটির কোনও রেফারেন্স না দিয়েই এই ভেক্টরের রেফারেন্স পেতে পারে।

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

এটি স্থানীয়ভাবে একই কাজ করতে পারে vector(উদাহরণস্বরূপ বেস, শেষের আকার এবং শেষ_ক্যাপাসিটি পয়েন্টারগুলির জন্য))

আইএসও C99 এই সমস্যাটির জন্য একটি সমাধান আছে: int *restrict foo। অনেক সি ++ প্রনয়ন সমর্থন int *__restrict fooদিচ্ছি যে মেমরি দ্বারা নির্দিষ্ট fooকরা হয় শুধুমাত্র যে পয়েন্টার মাধ্যমে অ্যাক্সেস। 2 টি অ্যারে নেওয়া ফাংশনে সর্বাধিক কার্যকর এবং আপনি যে সংকলকটি ওভারল্যাপ করে না সেগুলি প্রতিশ্রুতি দিতে চান। সুতরাং এটি পরীক্ষা করে কোডটি উত্পন্ন না করে অটো-ভেক্টরাইজ করতে পারে এবং এটির জন্য একটি ফ্যালব্যাক লুপ চালাতে পারে।

ওপি মন্তব্য করেছে:

মরিচায় একটি অ-পরিবর্তনীয় রেফারেন্স হ'ল একটি গ্লোবাল গ্যারান্টি যে আপনার রেফারেন্সের মানটি অন্য কেউ রূপান্তর করছে না (সি ++ এর সমতুল্য restrict)

এটি ব্যাখ্যা করে কেন মরিচা এই অপ্টিমাইজেশন করতে পারে তবে সি ++ করতে পারে না।


আপনার সি ++ অনুকূল করা

স্পষ্টতই auto size = input.size();আপনার ফাংশনের শীর্ষে একবার ব্যবহার করা উচিত যাতে সংকলকটি জানে যে এটি লুপের আক্রমণকারী। সি ++ বাস্তবায়ন আপনার জন্য এই সমস্যাটি সমাধান করে না, তাই আপনাকে এটি নিজেই করতে হবে।

const int *data = input.data();আপনার std::vector<int>"কন্ট্রোল ব্লক" থেকেও ডেটা পয়েন্টার লোড উত্তোলনের প্রয়োজন হতে পারে । এটি দুর্ভাগ্যজনক যে অনুকূলকরণের জন্য খুব অ-idiomatic উত্স পরিবর্তন প্রয়োজন হতে পারে।

মরিচা অনেক বেশি আধুনিক ভাষা, সংকলক বিকাশকারীগণ কম্পাইলারদের জন্য অনুশীলনে কী সম্ভব তা শিখার পরে ডিজাইন করেছিলেন। এটি সত্যিই অন্যান্য উপায়েও দেখায়, সিপিইউগুলির মাধ্যমে কিছু সহজে চালানো i32.count_ones, আবর্তন, বিট-স্ক্যান ইত্যাদির মতো বহনযোগ্যভাবে এক্সপোজিট করা সহ এটি সত্যই বোবা যে আইএসও সি ++ এর পরেও এগুলির কোনও বহনযোগ্য নয়, ব্যতীত std::bitset::count()


1
যদি ভেক্টরকে বাই-ভ্যালু নেওয়া হয় তবে ওপির কোডটিতে এখনও পরীক্ষা রয়েছে। সুতরাং যদিও জিসিসি সে ক্ষেত্রে অনুকূলিত করতে পারে তবে তা তা করে না।
আখরোট

1
মান operator<<সেই অপারেন্ড ধরণের আচরণের সংজ্ঞা দেয় ; সুতরাং স্ট্যান্ডার্ড সি ++ এ এটি কোনও ব্ল্যাক বক্স নয় এবং সংকলক এটি ধরে নিতে পারে যে ডকুমেন্টেশন যা বলে তা তাই করে। সম্ভবত তারা মানহীন আচরণ যুক্ত করে পাঠাগার বিকাশকারীদের সমর্থন করতে চান ...
এমএম

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

2
@ এমএম এটি এলোমেলো বস্তু বলেনি, আমি বলেছিলাম একটি বাস্তবায়ন সংজ্ঞাযুক্ত ভেক্টর। স্ট্যান্ডার্ডে এমন কিছুই নেই যা বাস্তবায়ন সংজ্ঞায়িত ভেক্টর ব্যবহার করে যা কোনও অপারেটর << সংশোধন করে এবং প্রয়োগকে সংজ্ঞায়িত পদ্ধতিতে এই ভেক্টরটিতে অ্যাক্সেসের অনুমতি দেয় এমন কোনও প্রয়োগকে নিষিদ্ধ করে। coutথেকে প্রাপ্ত উত্সর্গীকৃত কোনও ব্যবহারকারী সংজ্ঞায়িত শ্রেণীর কোনও অবজেক্টটি streambufব্যবহার করে স্ট্রিমের সাথে যুক্ত হতে দেয় cout.rdbuf। একইভাবে উদ্ভূত একটি বস্তুর ostreamসাথে যুক্ত হতে পারে cout.tie
রস রিজ

2
@ পিটারকর্ডস - আমি স্থানীয় ভেক্টর সম্পর্কে এতটা আত্মবিশ্বাসী থাকব না: যে কোনও সদস্যের কার্যকারিতা বাইরে চলে যাওয়ার সাথে সাথে স্থানীয়রা কার্যকরভাবে পালিয়ে গেছে কারণ thisপয়েন্টারটি স্পষ্টভাবে পাস করেছে। এটি কনস্ট্রাক্টর হিসাবে প্রাথমিকভাবে অনুশীলনে ঘটতে পারে। বিবেচনা করুন এই সহজ লুপ - আমি শুধুমাত্র জিসিসি প্রধান লুপ (থেকে চেক করা L34:থেকে jne L34), কিন্তু এটা স্পষ্টভাবে যেন ভেক্টর সদস্যদের পালিয়ে গেছে (প্রতিটি পুনরাবৃত্তির মেমরি থেকে তাদের লোড হচ্ছে) ব্যবহার করা হয়।
বিওনরোপ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.