java - কেন যদি(variable1% variable2== 0) অকার্যকর?




performance (3)

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

long progressCheck = 50000;

long counter = progressCheck;

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

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

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

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

আমি জাভাতে নতুন, এবং গত রাতে কিছু কোড চলমান ছিল, এবং এই সত্যিই আমাকে বিরক্ত। আমি লুপের জন্য প্রতিটি এক্স আউটপুট প্রদর্শনের জন্য একটি সাধারণ প্রোগ্রাম তৈরি করছিলাম এবং আমি কার্যক্ষমতার মধ্যে একটি গুরুতর হ্রাস লক্ষ্য করেছি, যখন আমি মডুলাসটি 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 মিনিট নেয় এবং অন্যটি 1২ সেকেন্ড বা তার মতো কিছু নেয়। আপনাকে stopNum বৃদ্ধি করতে stopNum বা progressCheck হ্রাস করতে হবে যদি আপনার পিসি আমার চেয়ে বেশি দক্ষ না হয় বা না।

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

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

EDIT2: আমি আজ রাতে আরেকটি পরীক্ষা করতে যাচ্ছি, যেখানে মডুলাসের পরিবর্তে, এটি কেবলমাত্র একটি পরিবর্তনশীল বৃদ্ধি করে এবং যখন এটি অগ্রগতিতে পৌঁছে যায় তখন চেকটি এক সঞ্চালন করবে এবং তারপরে তৃতীয়টি বিকল্পের জন্য সেই পরিবর্তনশীলটিকে 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 ms (50000 ব্যবহার করার সময় ~ 1000ms ছিল)
  • বৃদ্ধি = 1904 মি
  • সংক্ষিপ্ত রূপান্তর = 679 মি

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

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

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


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

OSR স্টাব কম্পাইল করা পদ্ধতির একটি বিশেষ সংস্করণ যা বিশেষভাবে ইন্টারপ্রেটেড মোড থেকে সংকলিত কোড থেকে প্রক্রিয়া চলাকালীন সঞ্চালনের জন্য হস্তান্তর করার উদ্দেশ্যে তৈরি।

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

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

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

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

@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 প্রথম পুনরাবৃত্তি প্রকৃতপক্ষে অনেক ধীর। কিন্তু যত তাড়াতাড়ি পদ্ধতিটি শুরু থেকে পুনরূদ্ধার হয়, নতুন অসংলগ্ন সংস্করণটি কার্যকর হয় যা সমস্ত উপলব্ধ কম্পাইলার অপ্টিমাইজেশানগুলি প্রদান করে।


@ Phuclv comment ফলো-আপে, আমি জিট 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






performance