দৈর্ঘ্যের 2 টি অ্যারেতে কোডের নিম্নোক্ত দুটি স্নিপেটগুলি বিবেচনা করুন:
boolean isOK(int i) {
for (int j = 0; j < filters.length; ++j) {
if (!filters[j].isOK(i)) {
return false;
}
}
return true;
}
এবং
boolean isOK(int i) {
return filters[0].isOK(i) && filters[1].isOK(i);
}
আমি ধরে নেব যে যথেষ্ট উষ্ণতার পরে এই দুটি টুকরোটির পারফরম্যান্স একই হওয়া উচিত।
আমি যেমন এখানে এবং এখানে বর্ণিত জেএমএইচ মাইক্রো-বেঞ্চমার্কিং ফ্রেমওয়ার্ক ব্যবহার করে এটি পরীক্ষা করেছি এবং লক্ষ্য করেছি যে দ্বিতীয় স্নিপেটটি 10% এরও বেশি দ্রুত is
প্রশ্ন: কেন জাভা আমার প্রথম স্নিপেটটি বেসিক লুপ আন্রোলিং কৌশলটি ব্যবহার করে অনুকূলিত করে নি?
বিশেষত, আমি নিম্নলিখিতগুলি বুঝতে চাই:
- আমি সহজে একটি কোড 2 ফিল্টার মামলা জন্য অনুকূল এবং এখনও ফিল্টার অন্য একটি নম্বর ক্ষেত্রে কাজ করতে পারেন যে (একটি সহজ রচয়িতা কল্পনা) তৈরী করতে পারে:
return (filters.length) == 2 ? new FilterChain2(filters) : new FilterChain1(filters)
। জেআইটিসিও কি একই কাজ করতে পারে এবং তা না হলে কেন? - জেআইটিসি কি সনাক্ত করতে পারে যে ' ফিল্টারস দৈর্ঘ্য == 2 ' সবচেয়ে ঘন ঘন কেস এবং কিছু ওয়ার্ম-আপ করার পরে এই মামলার অনুকূল যে কোডটি তৈরি করতে পারে? এটি ম্যানুয়ালি-নথিভুক্ত সংস্করণ হিসাবে প্রায় অনুকূল হতে হবে।
- জেআইটিসি সনাক্ত করতে পারে যে কোনও নির্দিষ্ট উদাহরণ খুব ঘন ঘন ব্যবহৃত হয় এবং তারপরে এই নির্দিষ্ট উদাহরণের জন্য একটি কোড তৈরি করতে পারে (যার জন্য এটি জানেন যে ফিল্টারগুলির সংখ্যা সর্বদা 2)?
আপডেট: একটি উত্তর পেয়েছে যে জেআইটিসি কেবল একটি শ্রেণির স্তরে কাজ করে। ঠিক আছে বুঝেছি.
আদর্শভাবে, আমি জেআইটিসি কীভাবে কাজ করে তার গভীর বোঝার সাথে কারও কাছ থেকে উত্তর পেতে চাই।
বেঞ্চমার্ক চালানোর বিশদ:
- জাভা 8 ওপেনজেডিকে এবং ওরাকল হটস্পটের সর্বশেষ সংস্করণে চেষ্টা করা, ফলাফলগুলি একই রকম
- ব্যবহৃত জাভা পতাকা: -Xmx4g -Xms4g -server -Xbatch -XX: CICompilerCount = 2 (অভিনব পতাকা ছাড়াও একই রকম ফলাফল পেয়েছে)
- যাইহোক, আমি যদি একইভাবে একটি লুপ (জেএমএইচ দিয়ে নয়) কয়েক বিলিয়ন বার চালিত করি, তবে দ্বিতীয় স্নিপেটটি সর্বদা পরিষ্কারভাবে দ্রুত হয়
সাধারণ বেঞ্চমার্ক আউটপুট:
বেঞ্চমার্ক (ফিল্টারইনডেক্স) মোড সিএনটি স্কোর ত্রুটি ইউনিট
লুপউনরোলিংবেঞ্চমার্ক.আরুনবঞ্চমার্ক 0 গড় 400 44.202 ± 0.224 এনএস / অপ
লুপউনرول্লিংবেঞ্চমার্ক.আরুনবেঞ্চমার্ক 1 গড় 400 38.347 ± 0.063 এনএস / ওপেন
(প্রথম লাইনটি প্রথম স্নিপেটের সাথে মিলিত হয়, দ্বিতীয় লাইন - দ্বিতীয়টিতে।
সম্পূর্ণ বেঞ্চমার্ক কোড:
public class LoopUnrollingBenchmark {
@State(Scope.Benchmark)
public static class BenchmarkData {
public Filter[] filters;
@Param({"0", "1"})
public int filterIndex;
public int num;
@Setup(Level.Invocation) //similar ratio with Level.TRIAL
public void setUp() {
filters = new Filter[]{new FilterChain1(), new FilterChain2()};
num = new Random().nextInt();
}
}
@Benchmark
@Fork(warmups = 5, value = 20)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public int runBenchmark(BenchmarkData data) {
Filter filter = data.filters[data.filterIndex];
int sum = 0;
int num = data.num;
if (filter.isOK(num)) {
++sum;
}
if (filter.isOK(num + 1)) {
++sum;
}
if (filter.isOK(num - 1)) {
++sum;
}
if (filter.isOK(num * 2)) {
++sum;
}
if (filter.isOK(num * 3)) {
++sum;
}
if (filter.isOK(num * 5)) {
++sum;
}
return sum;
}
interface Filter {
boolean isOK(int i);
}
static class Filter1 implements Filter {
@Override
public boolean isOK(int i) {
return i % 3 == 1;
}
}
static class Filter2 implements Filter {
@Override
public boolean isOK(int i) {
return i % 7 == 3;
}
}
static class FilterChain1 implements Filter {
final Filter[] filters = createLeafFilters();
@Override
public boolean isOK(int i) {
for (int j = 0; j < filters.length; ++j) {
if (!filters[j].isOK(i)) {
return false;
}
}
return true;
}
}
static class FilterChain2 implements Filter {
final Filter[] filters = createLeafFilters();
@Override
public boolean isOK(int i) {
return filters[0].isOK(i) && filters[1].isOK(i);
}
}
private static Filter[] createLeafFilters() {
Filter[] filters = new Filter[2];
filters[0] = new Filter1();
filters[1] = new Filter2();
return filters;
}
public static void main(String[] args) throws Exception {
org.openjdk.jmh.Main.main(args);
}
}
@Setup(Level.Invocation)
: নিশ্চিত নয় যে এটি সাহায্য করে (জাভাদোকটি দেখুন)।
final
কিন্তু জে আই টি JIT দেখে না যে, সমস্ত উদাহরণ ক্লাসের দৈর্ঘ্য 2. একটি অ্যারের পাবে দেখার জন্য, তা ডুব করতে হবে createLeafFilters()
পদ্ধতিটি এবং কোডটি গভীরভাবে বিশ্লেষণ করে শিখুন যে অ্যারে সর্বদা 2 দীর্ঘ হবে। আপনি কেন বিশ্বাস করেন যে জেআইটি অপ্টিমাইজার আপনার কোডের গভীরে ডুব দেবে?