(ভেরিয়েবল 1% ভেরিয়েবল 2 == 0) অকার্যকর কেন?


179

আমি জাভাতে নতুন, এবং গতরাতে কিছু কোড চালাচ্ছিলাম এবং এটি আমাকে সত্যিই বিরক্ত করেছিল। আমি লুপ জন্য একটি প্রতিটি এক্স আউটপুট প্রদর্শন করতে একটি সহজ প্রোগ্রাম তৈরী করতে লাগলেন, আর আমি কর্মক্ষমতা একটি বিশাল হ্রাস যখন আমি যেমন মডুলাস ব্যবহার লক্ষ্য variable % variableবনাম variable % 5000বা যে কোন বস্তু। কেউ আমাকে ব্যাখ্যা করতে পারেন যে এটি কেন এবং এর কারণ কী? তাই আমি আরও ভাল হতে পারি ...

এখানে "দক্ষ" কোডটি রয়েছে (দুঃখিত যদি আমি কিছুটা সিনট্যাক্স ভুল করে ফেলেছি তবে আমি এখনই কোডটি কম্পিউটারে নেই)

long startNum = 0;
long stopNum = 1000000000L;

for (long i = startNum; i <= stopNum; i++){
    if (i % 50000 == 0) {
        System.out.println(i);
    }
}

এখানে "অদক্ষ কোড"

long startNum = 0;
long stopNum = 1000000000L;
long progressCheck = 50000;

for (long i = startNum; i <= stopNum; i++){
    if (i % progressCheck == 0) {
        System.out.println(i);
    }
}

মনে মনে আমি পার্থক্যগুলি পরিমাপ করার জন্য আমার একটি তারিখের পরিবর্তনশীল ছিল এবং এটি যথেষ্ট দীর্ঘ হয়ে গেলে, প্রথমটিরটি 50 মিলিয়ন হয়ে যায় অন্যটি 12 সেকেন্ড বা এর মতো কিছু নেয়। আপনার পিসি আমার চেয়ে বেশি দক্ষ কিনা বা কী না তা আপনাকে বাড়িয়ে দিতে stopNumবা হ্রাস করতে হতে পারে progressCheck

আমি ওয়েবে চারপাশে এই প্রশ্নটি চেয়েছিলাম, কিন্তু আমি কোনও উত্তর খুঁজে পাই না, সম্ভবত আমি এটি ঠিক জিজ্ঞাসা করছি না।

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

সম্পাদনা 2: আমি আজ রাতে আরেকটি পরীক্ষা করতে যাচ্ছি, যেখানে মডুলাসের পরিবর্তে এটি কেবল একটি পরিবর্তনশীল বৃদ্ধি করে এবং যখন এটি প্রগ্রেস চেক এ পৌঁছায়, তখন এটি একটি সম্পাদন করবে, এবং তৃতীয় বিকল্পের জন্য 0 পরিবর্তন করতে হবে।

EDIT3.5:

আমি এই কোডটি ব্যবহার করেছি, এবং নীচে আমি আমার ফলাফলগুলি দেখাবো .. দুর্দান্ত সহায়তার জন্য আপনাকে সমস্ত ধন্যবাদ! আমি দীর্ঘ থেকে 0 এর সংক্ষিপ্ত মানটির তুলনা করার চেষ্টাও করেছি, সুতরাং আমার সমস্ত নতুন চেক এটিকে পুনরাবৃত্তিতে সমান করে তুলতে "65536" বার হয়।

public class Main {


    public static void main(String[] args) {

        long startNum = 0;
        long stopNum = 1000000000L;
        long progressCheck = 65536;
        final long finalProgressCheck = 50000;
        long date;

        // using a fixed value
        date = System.currentTimeMillis();
        for (long i = startNum; i <= stopNum; i++) {
            if (i % 65536 == 0) {
                System.out.println(i);
            }
        }
        long final1 = System.currentTimeMillis() - date;
        date = System.currentTimeMillis();
        //using a variable
        for (long i = startNum; i <= stopNum; i++) {
            if (i % progressCheck == 0) {
                System.out.println(i);
            }
        }
        long final2 = System.currentTimeMillis() - date;
        date = System.currentTimeMillis();

        // using a final declared variable
        for (long i = startNum; i <= stopNum; i++) {
            if (i % finalProgressCheck == 0) {
                System.out.println(i);
            }
        }
        long final3 = System.currentTimeMillis() - date;
        date = System.currentTimeMillis();
        // using increments to determine progressCheck
        int increment = 0;
        for (long i = startNum; i <= stopNum; i++) {
            if (increment == 65536) {
                System.out.println(i);
                increment = 0;
            }
            increment++;

        }

        //using a short conversion
        long final4 = System.currentTimeMillis() - date;
        date = System.currentTimeMillis();
        for (long i = startNum; i <= stopNum; i++) {
            if ((short)i == 0) {
                System.out.println(i);
            }
        }
        long final5 = System.currentTimeMillis() - date;

                System.out.println(
                "\nfixed = " + final1 + " ms " + "\nvariable = " + final2 + " ms " + "\nfinal variable = " + final3 + " ms " + "\nincrement = " + final4 + " ms" + "\nShort Conversion = " + final5 + " ms");
    }
}

ফলাফল:

  • স্থির = 874 এমএস (সাধারণত 1000 মিমি প্রায়, তবে এটি 2 পাওয়ার শক্তি হওয়ার কারণে দ্রুত)
  • পরিবর্তনশীল = 8590 এমএস
  • চূড়ান্ত পরিবর্তনশীল = 1944 এমএস (50000 ব্যবহার করার সময় ms 1000ms ছিল)
  • বৃদ্ধি = 1904 এমএস
  • সংক্ষিপ্ত রূপান্তর = 679 এমএস

যথেষ্ট আশ্চর্যজনক নয়, বিভাগের অভাবের কারণে সংক্ষিপ্ত রূপান্তরটি "দ্রুত" পথে 23% দ্রুত ছিল was এটি লক্ষণীয় আকর্ষণীয়। আপনার যদি প্রতি 256 বার (বা সেখানে প্রায়) কিছু দেখাতে বা তুলনা করার দরকার হয় তবে আপনি এটি করতে পারেন এবং ব্যবহার করতে পারেন

if ((byte)integer == 0) {'Perform progress check code here'}

একটি ফাইনাল ইন্টারেস্টিং নোট, 65536 (একটি চমত্কার সংখ্যা নয়) সহ "ফাইনাল ঘোষিত ভেরিয়েবল" তে মডিউল ব্যবহার করে স্থির মানের চেয়ে ধীর গতি (ধীর) ছিল। যেখানে আগে এটি একই গতির কাছে বেঞ্চমার্কিং ছিল।


29
আমি আসলে একই ফলাফল পেয়েছি। আমার মেশিনে, প্রথম লুপটি প্রায় 1,5 সেকেন্ডে চালিত হয় এবং দ্বিতীয়টি প্রায় 9 সেকেন্ডে চালায়। আমি যদি ভেরিয়েবলের finalসামনে যুক্ত করি তবে progressCheckউভয়ই আবার একই গতিতে চলে। এটি আমাকে বিশ্বাস করতে পরিচালিত করে যে সংকলক বা জেআইটি যখন লুপটি progressCheckস্থির করে তা লুপটিকে অনুকূলিত করে তোলে is
marstran


24
একটি ধ্রুবক দ্বারা বিভাগ সহজেই গুণিত বিপরীত দ্বারা একটি গুণে রূপান্তর করা যেতে পারে । একটি ভেরিয়েবল দ্বারা বিভাগ করতে পারে না। এবং একটি 32-বিট বিভাজন x86
ফুক্লভিভি

2
@ ফুকলভ নোট 32-বিট বিভাজন এখানে সমস্যা নয়, এটি উভয় ক্ষেত্রেই 64-বিটের অবশিষ্ট অপারেশন
ব্যবহারকারী 85421

4
@ রবার্টকোটারম্যান আপনি যদি ভেরিয়েবলটিকে চূড়ান্ত হিসাবে ঘোষণা করেন তবে সংকলক ধ্রুবক (গ্রহ / জাভা ১১) ((ভেরিয়েবলের জন্য আরও একটি মেমরি স্লট ব্যবহার করা সত্ত্বেও) ব্যবহার করে) একই বাইটকোড তৈরি করে
ইউজার 85421

উত্তর:


139

আপনি ওএসআর (অন-স্ট্যাক রিপ্লেসমেন্ট) স্টাব পরিমাপ করছেন ।

ওএসআর স্টাব হ'ল সংকলিত পদ্ধতির একটি বিশেষ সংস্করণ যা বিশেষত পদ্ধতিটি চলমান থাকাকালীন সংক্ষেপিত মোড থেকে সংকলিত কোডে সম্পাদন স্থানান্তর করার উদ্দেশ্যে উদ্দিষ্ট।

ওএসআর স্টাবগুলি নিয়মিত পদ্ধতির মতো অপটিমাইজড হয় না, কারণ তাদের ব্যাখ্যাযুক্ত ফ্রেমের সাথে সামঞ্জস্য করা একটি ফ্রেম বিন্যাস প্রয়োজন। আমি ইতিমধ্যে নিম্নলিখিত উত্তরগুলিতে এটি দেখিয়েছি: 1 , 2 , 3

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

বিশেষ করে এর মানে জে আই টি JIT প্রতিস্থাপন করে না বিভাজন পূর্ণসংখ্যা সঙ্গে গুণ । (দেখুন জিসিসি কেন পূর্ণসংখ্যা বিভাগ বাস্তবায়নে কোনও অদ্ভুত সংখ্যার দ্বারা গুণন ব্যবহার করে? সামনের সময়ের সংকলক থেকে অ্যাস ট্রিকের জন্য, যখন মানগুলি ইনলাইনিং / ধ্রুবক-প্রচারের পরে একটি সংকলন-সময় ধ্রুবক হয়, যদি সেই অপটিমাইজেশন সক্ষম করা থাকে) .প্রকাশের একটি পূর্ণসংখ্যার আক্ষরিক অধিকারও এটি %অপ্টিমাইজড হয়ে যায় gcc -O0, এটি এখানে যেমন ওএসআর স্টাবের মধ্যেও জিতার দ্বারা অনুকূলিত হয়)

তবে, আপনি যদি একই পদ্ধতিটি বেশ কয়েকবার চালান, তবে দ্বিতীয় এবং পরবর্তী রানগুলি নিয়মিত (নন-ওএসআর) কোডটি কার্যকর করে, যা সম্পূর্ণরূপে অনুকূলিত is তত্ত্বটি প্রমাণ করার জন্য এখানে একটি মানদণ্ড রয়েছে ( জেএমএইচ ব্যবহার করে বেঞ্চমার্ক ):

@State(Scope.Benchmark)
public class Div {

    @Benchmark
    public void divConst(Blackhole blackhole) {
        long startNum = 0;
        long stopNum = 100000000L;

        for (long i = startNum; i <= stopNum; i++) {
            if (i % 50000 == 0) {
                blackhole.consume(i);
            }
        }
    }

    @Benchmark
    public void divVar(Blackhole blackhole) {
        long startNum = 0;
        long stopNum = 100000000L;
        long progressCheck = 50000;

        for (long i = startNum; i <= stopNum; i++) {
            if (i % progressCheck == 0) {
                blackhole.consume(i);
            }
        }
    }
}

এবং ফলাফল:

# Benchmark: bench.Div.divConst

# Run progress: 0,00% complete, ETA 00:00:16
# Fork: 1 of 1
# Warmup Iteration   1: 126,967 ms/op
# Warmup Iteration   2: 105,660 ms/op
# Warmup Iteration   3: 106,205 ms/op
Iteration   1: 105,620 ms/op
Iteration   2: 105,789 ms/op
Iteration   3: 105,915 ms/op
Iteration   4: 105,629 ms/op
Iteration   5: 105,632 ms/op


# Benchmark: bench.Div.divVar

# Run progress: 50,00% complete, ETA 00:00:09
# Fork: 1 of 1
# Warmup Iteration   1: 844,708 ms/op          <-- much slower!
# Warmup Iteration   2: 105,893 ms/op          <-- as fast as divConst
# Warmup Iteration   3: 105,601 ms/op
Iteration   1: 105,570 ms/op
Iteration   2: 105,475 ms/op
Iteration   3: 105,702 ms/op
Iteration   4: 105,535 ms/op
Iteration   5: 105,766 ms/op

divVarঅযোগ্যভাবে সংকলিত ওএসআর স্টাবের কারণে প্রথম প্রথম পুনরাবৃত্তিটি অনেক ধীরে ধীরে। তবে পদ্ধতিটি শুরু থেকে পুনরায় চালু হওয়ার সাথে সাথেই নতুন অযাচিত সংস্করণ কার্যকর করা হয় যা উপলব্ধ সমস্ত সংকলক অপ্টিমাইজেশনের জন্য উপকৃত হয়।


5
আমি এই বিষয়ে ভোট দিতে দ্বিধা করি। একদিকে, "আপনি আপনার বেঞ্চমার্ককে গণ্ডগোল করেছেন, জেআইটি সম্পর্কে কিছু পড়ুন" বলার বিস্তৃত উপায় বলে মনে হচ্ছে। অন্যদিকে, আমি অবাক হয়েছি যে আপনি কেন এতটাই নিশ্চিত যে ওএসআর এখানে মূল প্রাসঙ্গিক বিষয় point আমি বলতে চাচ্ছি, একটি (মাইক্রো) করছেন বেঞ্চমার্ক জড়িত যে System.out.printlnপ্রায় হবে অগত্যা আবর্জনা ফল, এবং সত্য উভয় সংস্করণ সমানভাবে দ্রুত OSR নিয়ে কিছু করতে নেই বিশেষ হিসাবে আমি বলতে পারেন .., যতটা
Marco13

2
(আমি কৌতূহলী এবং এটি বুঝতে পছন্দ করি I আমি আশা করি মন্তব্যগুলি বিঘ্নিত নয়, পরে এগুলি মুছে ফেলতে পারে তবে:) লিঙ্কটি 1কিছুটা সন্দেহজনক - খালি লুপটিও পুরোপুরি অপ্টিমাইজ করা যেতে পারে। দ্বিতীয়টির সাথে আরও মিল রয়েছে। তবে আবার, এটি স্পষ্ট নয় যে আপনি কেন ওএসআরটির সাথে বিশেষত পার্থক্যটি বিশেষত গুণান । আমি কেবল বলেছিলাম: এক পর্যায়ে, পদ্ধতিটি জেআইটিড হয় এবং দ্রুত হয়। আমার বোঝার জন্য, ওএসআর কেবলমাত্র চূড়ান্ত, অনুকূলিত কোডটি ব্যবহার করতে পারে (মোটামুটিভাবে) next "পরবর্তী অপ্টিমাইজেশন পাসের জন্য পিছিয়ে"। (অবিরত ...)
মার্কো 13

1
(অব্যাহত :) আপনি হটস্পট লগগুলিকে বিশদভাবে বিশ্লেষণ না করে আপনি পারছেন না যে পার্থক্যটি জেটিড এবং আন-জেআইটিড কোডের সাথে তুলনা করে, বা জেআইটিআইডি এবং ওএসআর-স্টাব-কোডের সাথে তুলনা করে সৃষ্টি হয়েছে। এবং আপনি অবশ্যই নিশ্চিতভাবে বলতে পারবেন না যে প্রশ্নটিতে আসল কোড বা একটি সম্পূর্ণ জেএমএইচ বেঞ্চমার্ক থাকবে না। সুতরাং যুক্তি দিয়ে যে পার্থক্যটি ওএসআর শোনার কারণে ঘটেছিল, আমার পক্ষে, অনুপযুক্তভাবে নির্দিষ্ট (এবং "বেআইনী") যে তুলনায় এটি সাধারণভাবে জেআইটির মাধ্যমে ঘটেছিল তা তুলনা করে। (কোনও অপরাধ নেই - আমি কেবল ভাবছি ...)
মার্কো 13

4
@ মার্কো 13 একটি সাধারণ উপাচার্য রয়েছে: জেআইটির কার্যকলাপ ব্যতীত প্রতিটি %অপারেশনের একই ওজন হবে, কারণ একটি অপ্টিমাইজার সত্যিকারের কাজ করে, তবে একটি অপ্টিমাইজড এক্সিকিউশন কেবল সম্ভব, ভাল। সুতরাং একটি লুপের বৈকল্পিক অপরটি অপ্টিমাইজারের উপস্থিতি প্রমাণ করে এবং আরও প্রমাণ করে যে এটি একটি লুপকে অন্যটির (একই পদ্ধতির মধ্যে!) একই ডিগ্রীতে অপ্টিমাইজ করতে ব্যর্থ হয়েছিল। যেহেতু এই উত্তরটি উভয় লুপকে একই ডিগ্রীতে অনুকূলকরণের সক্ষমতা প্রমাণ করে, তাই এমন কিছু অবশ্যই থাকতে হবে যা অপ্টিমাইজেশনের ক্ষেত্রে বাধা ছিল। এবং এটি সমস্ত ক্ষেত্রে 99.9% এ ওএসআর
হোলার

4
@ মার্কো 13 হটস্পট রানটাইমের জ্ঞান এবং এর আগে একই জাতীয় সমস্যা বিশ্লেষণের অভিজ্ঞতার ভিত্তিতে এটি একটি "শিক্ষিত অনুমান"। এই ধরনের দীর্ঘ লুপটি ওএসআর ব্যতীত অন্যভাবে খুব কমই সংকলিত হতে পারে, বিশেষত একটি সাধারণ হাতে তৈরি বেঞ্চমার্কে। এখন, যখন ওপি সম্পূর্ণ কোড পোস্ট করেছে, আমি কেবল আবার কোডটি চালিয়ে যুক্তির বিষয়টি নিশ্চিত করতে পারি -XX:+PrintCompilation -XX:+TraceNMethodInstalls
অপাঙ্গীন

42

ফলো-আপ করার জন্য @phuclv মন্তব্য , আমি জে আই টি JIT দ্বারা উত্পন্ন কোড চেক করা 1 , ফলাফল নিম্নরূপ আছেন:

জন্য variable % 5000(ধ্রুবক দ্বারা বিভাগ):

mov     rax,29f16b11c6d1e109h
imul    rbx
mov     r10,rbx
sar     r10,3fh
sar     rdx,0dh
sub     rdx,r10
imul    r10,rdx,0c350h    ; <-- imul
mov     r11,rbx
sub     r11,r10
test    r11,r11
jne     1d707ad14a0h

জন্য variable % variable:

mov     rax,r14
mov     rdx,8000000000000000h
cmp     rax,rdx
jne     22ccce218edh
xor     edx,edx
cmp     rbx,0ffffffffffffffffh
je      22ccce218f2h
cqo
idiv    rax,rbx           ; <-- idiv
test    rdx,rdx
jne     22ccce218c0h

বিভাগটি সবসময় গুণনের চেয়ে বেশি সময় নেয় বলে শেষ কোড স্নিপেট কম পারফর্মেন্ট হয়।

জাভা সংস্করণ:

java version "11" 2018-09-25
Java(TM) SE Runtime Environment 18.9 (build 11+28)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11+28, mixed mode)

1 - ভিএম বিকল্পগুলি ব্যবহৃত: -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,src/java/Main.main


14
"ধীর" উপর একটি মাত্রার অর্ডার দিতে, x86_64 এর জন্য: imul3 টি চক্র, idiv30 থেকে 90 চক্রের মধ্যে। সুতরাং পূর্ণসংখ্যার বিভাগটি 10x থেকে 30x এর মধ্যে পূর্ণসংখ্যার গুণণের চেয়ে ধীর হয়।
ম্যাথিউ এম।

2
আপনি আগ্রহী পাঠকদের জন্য এর সমস্ত অর্থ কী তা ব্যাখ্যা করতে পারেন, তবে এসেম্বলারের সাথে কথা বলছেন না?
নিকো হাজেস

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

4
@ এমব্রেডলি আপনার ইনপুটটির জন্য ধন্যবাদ, তবে এরূপ ব্যাখ্যাটি উত্তরের সাথে যুক্ত করা উচিত এবং মন্তব্য বিভাগে লুকানো উচিত নয়
নিকো হায়েস

6
@ এমব্রেডলি: মূল বিষয় হল, আধুনিক সিপিইউতে গুণগুলি দ্রুত কারণ আংশিক পণ্যগুলি স্বতন্ত্র এবং এইভাবে পৃথকভাবে গণনা করা যায়, যখন বিভাগের প্রতিটি স্তর পূর্ববর্তী পর্যায়ে নির্ভরশীল।
সুপারক্যাট

26

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

long progressCheck = 50000;

long counter = progressCheck;

for (long i = startNum; i <= stopNum; i++){
    if (--counter == 0) {
        System.out.println(i);
        counter = progressCheck;
    }
}

(ছোট্ট অপটিমাইজেশন প্রচেষ্টা হিসাবে আমরা এখানে প্রাক-হ্রাস-ডাউন কাউন্টার ব্যবহার করি কারণ অনেকগুলি আর্কিটেকচারের সাথে সাথে 0গাণিতিক ক্রিয়াকলাপের সাথে সাথে 0 টি নির্দেশনা / সিপিইউ চক্রের সাথে তুলনা করার প্রয়োজন হয় কারণ ALU এর পতাকাগুলি পূর্ববর্তী ক্রিয়াকলাপটি ইতিমধ্যে যথাযথভাবে সেট করা হয়েছিল A সংকলক, যাইহোক, আপনি লেখেন এমনকি যদি এই অপ্টিমাইজেশন স্বয়ংক্রিয়ভাবে করতে হবে if (counter++ == 50000) { ... counter = 0; }।)

খেয়াল করুন যে প্রায়শই আপনি সত্যিই মডুলাস চান না / কারণ আপনারা জানেন যে আপনার লুপের কাউন্টার ( i) বা যা যা কেবল কখনও 1 দ্বারা বাড়ানো হয়, এবং আপনি প্রকৃত অবশিষ্টটি মডুলাসটি আপনাকে দেবেন সে সম্পর্কে আপনি সত্যিই চিন্তা করেন না, কেবল দেখুন যদি ইনক্রিমেন্টিং-বাই-ওয়ান কাউন্টারটি কিছু মান দেয়।

আর একটি 'কৌশল' হ'ল দু'টি পাওয়ার / অফ সীমা ব্যবহার করা, উদাহরণস্বরূপ progressCheck = 1024;। মডুলাস দুটির শক্তিকে বিটওয়াইজ and, অর্থাৎ মাধ্যমে দ্রুত গণনা করা যায় if ( (i & (1024-1)) == 0 ) {...}। এটি খুব দ্রুত হওয়া উচিত এবং কিছু আর্কিটেকচারে counterউপরের স্পষ্টতাকে ছাড়িয়ে যেতে পারে ।


3
একটি স্মার্ট সংকলক এখানে লুপগুলি উল্টে দেবে। বা আপনি উত্স যে এটি করতে পারে। if()শরীর একটি বাইরের লুপ শরীর হয়ে, এবং বাইরের কাপড় if()ইনার লুপ শরীর হয়ে যে জন্য রান min(progressCheck, stopNum-i)পুনরাবৃত্তিও। সুতরাং শুরুতে এবং প্রতিবার counter0 এ পৌঁছনোর পরে আপনি লুপের long next_stop = i + min(progressCheck, stopNum-i);জন্য সেট আপ করতে পারেন for(; i< next_stop; i++) {}। এক্ষেত্রে অভ্যন্তরীণ লুপটি ফাঁকা এবং আশা করা যায় যে এটি সম্পূর্ণরূপে অপ্টিমাইজ করা উচিত, আপনারা উত্সটিতে এটি করতে পারেন এবং জিটরের পক্ষে এটি সহজ করতে পারেন, আপনার লুপটি i + = 50k এ হ্রাস করতে পারে।
পিটার কর্ডেস

2
তবে হ্যাঁ, ফিজবজ / প্রগ্রেস চেক টাইপ স্টাফের জন্য সাধারণভাবে ডাউন-কাউন্টার একটি ভাল দক্ষ কৌশল।
পিটার কর্ডেস

আমি আমার প্রশ্নে যুক্ত করেছি, এবং একটি ইনক্রিমেন্ট করেছি, এটি --counterআমার ইনক্রিমেন্ট ভার্সনের মতোই দ্রুত, তবে কম কোড.ও এটির চেয়ে 1 কম ছিল, counter--আপনি যদি চান ঠিক নম্বরটি পাওয়া উচিত তবে আমি কৌতূহলী , এটি তার চেয়ে অনেক বেশি পার্থক্য নয়
রবার্ট কোটারম্যান

@ পিটারকর্ডস একটি স্মার্ট সংকলক কেবল সংখ্যা মুদ্রণ করবে, কোনও লুপ নেই। (আমি মনে করি যে 10 বছর আগে সম্ভবত কিছুটা আরও তুচ্ছ ব্যান্ডমার্ক সেভাবে ব্যর্থ হতে শুরু করেছিল))
পিটার - মনিকা ২

2
@ রবার্টকোটারম্যান হ্যাঁ, --counterএকে একে বন্ধ আছে। counter--আপনাকে হুবহু progressCheckসংখ্যা দেবে (অথবা আপনি progressCheck = 50001;অবশ্যই সেট করতে পারেন )।
জিমিবি

4

উপরের কোডগুলির পারফরম্যান্স দেখে আমিও অবাক হয়েছি। ঘোষিত ভেরিয়েবল অনুযায়ী প্রোগ্রামটি সম্পাদন করার জন্য সংকলকটি যে সময় নিয়েছিল তা সবই। দ্বিতীয় (অদক্ষ) উদাহরণে:

for (long i = startNum; i <= stopNum; i++) {
    if (i % progressCheck == 0) {
        System.out.println(i)
    }
}

আপনি দুটি ভেরিয়েবলের মধ্যে মডুলাস অপারেশন করছেন। এখানে, সংকলকটির মান পরীক্ষা করতে হবেstopNumprogressCheck প্রতিটি পুনরাবৃত্তির পরে প্রতিবার এই ভেরিয়েবলগুলির জন্য নির্দিষ্ট মেমরি ব্লকের করতে হবে এবং যেতে হবে কারণ এটি একটি পরিবর্তনশীল এবং এর মান পরিবর্তন হতে পারে।

এই কারণেই প্রতিটি পুনরাবৃত্তির সংকলক পরে ভেরিয়েবলের সর্বশেষ মানটি পরীক্ষা করতে মেমরির অবস্থানে যায়। তাই সংকলনের সময় সংকলক দক্ষ বাইট কোড তৈরি করতে সক্ষম হয়নি।

প্রথম কোড উদাহরণে, আপনি একটি ভেরিয়েবল এবং একটি ধ্রুবক সংখ্যাসূচক মানের মধ্যে মডিউলাস অপারেটর সম্পাদন করছেন যা কার্যকরকরণের মধ্যে পরিবর্তন হয় না এবং সংকলককে মেমরির অবস্থান থেকে সেই সংখ্যার মানটি পরীক্ষা করার প্রয়োজন হয় না। এজন্য সংকলক দক্ষ বাইট কোড তৈরি করতে সক্ষম হয়েছিল। আপনি যদি progressCheckএকটি হিসাবে finalবা final staticভেরিয়েবল হিসাবে ঘোষণা করেন তবে রান-টাইম / সংকলন-সময় সংকলনের সময় জেনে থাকুন যে এটি একটি চূড়ান্ত পরিবর্তনশীল এবং এর মান পরিবর্তন হচ্ছে না তবে সংকলকটি কোডের progressCheckসাথে প্রতিস্থাপন করুন 50000:

for (long i = startNum; i <= stopNum; i++) {
    if (i % 50000== 0) {
        System.out.println(i)
    }
}

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


1
একটি ট্রিলিয়ন বার আমি অপারেশনটি করে চলেছি তবুও একটি বিশাল পার্থক্য রয়েছে, তাই 1 ট্রিলিয়নেরও বেশি অপারেশন "দক্ষ" কোডটি করতে 89% সময় সাশ্রয় করেছে। মনে রাখবেন যদি আপনি এটি কয়েক হাজার বার করে থাকেন, এত ছোট পার্থক্যটি কথা বলছিলেন, এটি সম্ভবত কোনও বড় বিষয় নয়। মানে 1000 টিরও বেশি অপারেশন এটি আপনাকে 7 সেকেন্ডের 1 মিলিয়নতম সাশ্রয় করবে।
রবার্ট কোটারম্যান

1
@ বিশাল দুবে "উভয় কোড কার্যকর করার সময় তেমন তফাত হবে না।" প্রশ্ন পড়েছেন?
গ্রান্ট ফস্টার

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