উত্তর:
অবশ্যই হ্যাঁ. প্রতিবিম্বের মাধ্যমে একটি শ্রেণীর সন্ধান করা, প্রস্থের দ্বারা , আরও ব্যয়বহুল।
প্রতিবিম্বের উপর জাভার ডকুমেন্টেশন উদ্ধৃত :
যেহেতু প্রতিচ্ছবিতে এমন প্রকারগুলি অন্তর্ভুক্ত থাকে যা গতিশীলভাবে সমাধান করা হয়, কিছু জাভা ভার্চুয়াল মেশিন অপ্টিমাইজেশান সম্পাদন করা যায় না। ফলস্বরূপ, প্রতিফলিত অপারেশনগুলির তাদের অ-প্রতিবিম্বিত অংশগুলির তুলনায় ধীর পারফরম্যান্স থাকে এবং কোডের এমন কিছু অংশে এড়ানো উচিত যা কর্মক্ষমতা-সংবেদনশীল অ্যাপ্লিকেশনগুলিতে প্রায়শই বলা হয়।
সান জেআরই 6u10 চালিয়ে আমি আমার মেশিনে 5 মিনিটের মধ্যে হ্যাক করেছিলাম এমন একটি সহজ পরীক্ষা:
public class Main {
public static void main(String[] args) throws Exception
{
doRegular();
doReflection();
}
public static void doRegular() throws Exception
{
long start = System.currentTimeMillis();
for (int i=0; i<1000000; i++)
{
A a = new A();
a.doSomeThing();
}
System.out.println(System.currentTimeMillis() - start);
}
public static void doReflection() throws Exception
{
long start = System.currentTimeMillis();
for (int i=0; i<1000000; i++)
{
A a = (A) Class.forName("misc.A").newInstance();
a.doSomeThing();
}
System.out.println(System.currentTimeMillis() - start);
}
}
এই ফলাফলগুলির সাথে:
35 // no reflection
465 // using reflection
মনে রাখবেন অনুসন্ধান এবং ইনস্ট্যান্টেশন একসাথে হয়ে গেছে, এবং কিছু ক্ষেত্রে এই লুক অপসারণ করা যায় তবে এটি কেবল একটি প্রাথমিক উদাহরণ।
এমনকি আপনি কেবল তাত্ক্ষণিকভাবে চালিয়ে গেলেও, আপনি এখনও একটি পারফরম্যান্স হিট পাবেন:
30 // no reflection
47 // reflection using one lookup, only instantiating
আবার, ওয়াইএমএমভি।
হ্যাঁ, এটি ধীর।
তবে জঘন্য # 1 নিয়মটি মনে রাখবেন - প্রাক্কলন অপ্টিমাইজেশন হ'ল সমস্ত মন্দির মূল
(ভাল, ডিআরওয়াইয়ের জন্য # 1 দিয়ে বাঁধা হতে পারে)
আমি শপথ করছি, যদি কেউ আমার কাছে কাজ করে এসে আমাকে জিজ্ঞাসা করে থাকে তবে আমি তাদের কয়েক মাস পরের মাসগুলিতে খুব সচেতন থাকব।
আপনার এটির প্রয়োজন হওয়া নিশ্চিত হওয়া অবধি আপনার কখনই অনুকূলকরণ করা উচিত নয়, ততক্ষণ কেবল ভাল, পঠনযোগ্য কোডটি লিখুন।
ওহ, এবং আমি বোকা কোড লিখতে চাইছি না। আপনি সম্ভবত এটি করতে পারেন এমন সর্বোত্তম উপায় সম্পর্কে চিন্তা করুন - কোনও অনুলিপি এবং পেস্ট ইত্যাদি নেই (তারপরেও অভ্যন্তরীণ লুপগুলি এবং আপনার প্রয়োজনের সাথে সর্বোত্তমভাবে উপযুক্ত সংগ্রহটি ব্যবহারের বিষয়ে সতর্ক থাকুন - এগুলি উপেক্ষা করা "অপরিশোধিত" প্রোগ্রামিং নয় , এটি "খারাপ" প্রোগ্রামিং)
আমি যখন এই জাতীয় প্রশ্ন শুনতে পেলাম তবে তা ভুলে গিয়েছিলাম তবে আমি ভুলে গেছি যে সত্যই তা পাওয়ার আগে প্রত্যেককেই সমস্ত বিধি নিজেই শিখতে হবে। আপনি "অপটিমাইজড" কাউকে কিছুবার ডিবেগ করার জন্য মাসব্যাপী ব্যয় করার পরে আপনি এটি পাবেন।
সম্পাদনা করুন:
একটি আকর্ষণীয় জিনিস এই থ্রেডে ঘটেছে। # 1 উত্তর যাচাই করুন, কম্পাইলার জিনিসগুলির অনুকূলকরণে কতটা শক্তিশালী তা উদাহরণ example পরীক্ষাটি সম্পূর্ণরূপে অবৈধ কারণ অ-প্রতিবিম্বিত ইনস্ট্যান্টেশন সম্পূর্ণ ফ্যাক্টর আউট করা যায়।
পাঠ? আপনি একটি পরিষ্কার, ঝরঝরে কোডেড সমাধান না লিখে এবং এটি খুব ধীর হিসাবে প্রমাণিত না হওয়া পর্যন্ত কখনই অনুকূলিত হন না।
আপনি দেখতে পাবেন যে এভি = নতুন এ () জেভিএম দ্বারা অপ্টিমাইজ করা হচ্ছে। আপনি যদি বস্তুকে একটি অ্যারেতে রাখেন তবে তারা এত ভাল সম্পাদন করে না। ;) নিম্নলিখিত প্রিন্টগুলি ...
new A(), 141 ns
A.class.newInstance(), 266 ns
new A(), 103 ns
A.class.newInstance(), 261 ns
public class Run {
private static final int RUNS = 3000000;
public static class A {
}
public static void main(String[] args) throws Exception {
doRegular();
doReflection();
doRegular();
doReflection();
}
public static void doRegular() throws Exception {
A[] as = new A[RUNS];
long start = System.nanoTime();
for (int i = 0; i < RUNS; i++) {
as[i] = new A();
}
System.out.printf("new A(), %,d ns%n", (System.nanoTime() - start)/RUNS);
}
public static void doReflection() throws Exception {
A[] as = new A[RUNS];
long start = System.nanoTime();
for (int i = 0; i < RUNS; i++) {
as[i] = A.class.newInstance();
}
System.out.printf("A.class.newInstance(), %,d ns%n", (System.nanoTime() - start)/RUNS);
}
}
এটি আমার মেশিনে পার্থক্যটি প্রায় 150 এনএস এর পরামর্শ দেয়।
Class.getDeclaredMethod
) এবং তারপরে Method.invoke
একাধিকবার কল করি ? আমি একবারে প্রতিবার প্রতিফলনটি ব্যবহার করছি বা যতবার ডাকে? প্রশ্ন অনুসরণ করুন, যদি Method
এটির পরিবর্তে একটি হয় Constructor
এবং আমি Constructor.newInstance
একাধিকবার করি ?
যদি সত্যিই কিছু প্রয়োজনীয়তার দ্রুত প্রতিফলন বেশি, এবং এটি শুধু একটি অকাল অপ্টিমাইজেশান, দিয়ে তারপর বাইটকোড প্রজন্ম নয় এ এস এম বা একটি উচ্চ স্তরের গ্রন্থাগার একটি বিকল্প। প্রথমবারের মতো বাইটকোড তৈরি করা কেবল প্রতিবিম্ব ব্যবহারের চেয়ে ধীর, তবে একবার বাইকোড উত্পন্ন হওয়ার পরে এটি সাধারণ জাভা কোডের মতো দ্রুত এবং এটি জেআইটি সংকলক দ্বারা অনুকূলিত হবে।
কোড জেনারেশন ব্যবহার করে এমন অ্যাপ্লিকেশনগুলির কয়েকটি উদাহরণ:
সিজিএলআইবি দ্বারা উত্পাদিত প্রক্সিগুলিতে জাভির গতিশীল প্রক্সিগুলির তুলনায় কিছুটা দ্রুত গতিময় , কারণ সিজিআইএলআইবি তার প্রক্সিগুলির জন্য বাইকোড উত্পন্ন করে, তবে গতিশীল প্রক্সিগুলি কেবল প্রতিচ্ছবি ব্যবহার করে ( আমি সিজিএলআইবি মাপা পদ্ধতি কলগুলিতে প্রায় 10x দ্রুত হতে পেরেছি, তবে প্রক্সিগুলি তৈরি করা ধীর ছিল)।
জেরিয়াল প্রতিবিম্ব ব্যবহারের পরিবর্তে সিরিয়ালযুক্ত বস্তুর ক্ষেত্রগুলি পড়ার / লেখার জন্য বাইকোড উত্পন্ন করে। আছে কিছু benchmarks JSerial এর সাইটে।
আমি ১০০% নিশ্চিত নই (এবং এখন উত্সটি পড়ার মতো আমার মনে হয় না ) তবে আমি মনে করি গুইস নির্ভরতা ইনজেকশন দেওয়ার জন্য বাইকোড তৈরি করে। আমি ভুল হলে শুধরে.
"উল্লেখযোগ্য" সম্পূর্ণ প্রসঙ্গে নির্ভরশীল।
আপনি যদি কিছু কনফিগারেশন ফাইলের উপর ভিত্তি করে একটি একক হ্যান্ডলার অবজেক্ট তৈরি করতে প্রতিবিম্ব ব্যবহার করে থাকেন এবং তারপরে আপনার বাকী সময় ডাটাবেস অনুসন্ধান চালিয়ে ব্যয় করেন তবে তা তুচ্ছ নয়। যদি আপনি টাইট লুপে প্রতিবিম্বের মাধ্যমে বিপুল সংখ্যক অবজেক্ট তৈরি করেন তবে হ্যাঁ, এটি উল্লেখযোগ্য।
সাধারণভাবে, নকশার নমনীয়তা (যেখানে প্রয়োজন!) আপনার প্রতিবিম্বের ব্যবহার চালায়, না পারফরম্যান্স। যাইহোক, কর্মক্ষমতা একটি সমস্যা কিনা তা নির্ধারণ করার জন্য, আপনাকে আলোচনার ফোরাম থেকে স্বেচ্ছাসেবী প্রতিক্রিয়াগুলি না পেয়ে প্রোফাইল দেওয়া উচিত।
প্রতিবিম্ব সহ কিছু ওভারহেড রয়েছে তবে এটি আধুনিক ভিএমগুলিতে আগের তুলনায় অনেক ছোট।
আপনি যদি আপনার প্রোগ্রামে প্রতিটি সাধারণ বস্তু তৈরি করতে প্রতিবিম্ব ব্যবহার করে থাকেন তবে কিছু ভুল। মাঝে মাঝে এটি ব্যবহার করা যখন আপনার ভাল কারণ থাকে তখন মোটেও সমস্যা হওয়া উচিত নয়।
হ্যাঁ প্রতিবিম্বটি ব্যবহার করার সময় একটি পারফরম্যান্স হিট হয় তবে অনুকূলকরণের জন্য একটি সম্ভাব্য কাজের পদ্ধতিটি ক্যাশে করছে:
Method md = null; // Call while looking up the method at each iteration.
millis = System.currentTimeMillis( );
for (idx = 0; idx < CALL_AMOUNT; idx++) {
md = ri.getClass( ).getMethod("getValue", null);
md.invoke(ri, null);
}
System.out.println("Calling method " + CALL_AMOUNT+ " times reflexively with lookup took " + (System.currentTimeMillis( ) - millis) + " millis");
// Call using a cache of the method.
md = ri.getClass( ).getMethod("getValue", null);
millis = System.currentTimeMillis( );
for (idx = 0; idx < CALL_AMOUNT; idx++) {
md.invoke(ri, null);
}
System.out.println("Calling method " + CALL_AMOUNT + " times reflexively with cache took " + (System.currentTimeMillis( ) - millis) + " millis");
ফলাফল হবে:
[জাভা] কল করার পদ্ধতিটি 1000000 বার রিফ্লেক্সিভলি সাথে লুকোচুরির সাথে লেগেছিল 5618 মিলিস
[জাভা] ক্যাচ দিয়ে কলিং পদ্ধতিটি 1000000 বার রিফ্ল্যাক্সিভালি 270 মিলিস নিয়েছিল
প্রতিবিম্ব ধীর, যদিও বস্তুর বরাদ্দ প্রতিবিম্বের অন্যান্য দিকগুলির মতো হতাশ নয়। প্রতিবিম্ব-ভিত্তিক ইনস্ট্যান্টেশন সহ সমতুল্য পারফরম্যান্স অর্জনের জন্য আপনার কোডটি লিখতে হবে যাতে জিটটি বলতে পারে কোন শ্রেণিটি ইনস্ট্যান্ট করা হচ্ছে। যদি শ্রেণীর পরিচয় নির্ধারণ করা যায় না, তবে বরাদ্দ কোডটি ইনলাইন করা যায় না। সবচেয়ে খারাপ, পালানোর বিশ্লেষণ ব্যর্থ হয় এবং বস্তুকে স্ট্যাক-বরাদ্দ দেওয়া যায় না। আপনি যদি ভাগ্যবান হন তবে এই কোডটি গরম হয়ে উঠলে জেভিএম-র রান-টাইম প্রোফাইলিংটি উদ্ধার পেতে পারে এবং গতিশীলভাবে নির্ধারণ করতে পারে কোন শ্রেণিটি প্রাধান্য পায় এবং সেইটির জন্য অনুকূল হতে পারে।
সচেতন থাকুন যে এই থ্রেডের মাইক্রোব্যাঙ্কমার্কগুলি গভীরভাবে ত্রুটিযুক্ত রয়েছে, তাই তাদের সাথে লবণের দানা নিয়ে নিন। পিটার লরির সর্বদাই ত্রুটিযুক্ত: পদ্ধতিগুলি জিট করার জন্য এটি ওয়ার্মআপ চালায় এবং বরাদ্দ আসলেই ঘটছে কিনা তা নিশ্চিত করার জন্য এটি (সচেতনভাবে) পলায়ন বিশ্লেষণকে পরাস্ত করে। এমনকি এটির একটিরও সমস্যা রয়েছে যদিও: উদাহরণস্বরূপ, অ্যারে স্টোরের বিশাল সংখ্যক ক্যাশে এবং স্টোর বাফারকে পরাস্ত করতে পারে বলে আশা করা যায়, তাই আপনার বরাদ্দ খুব দ্রুত হলে এটি বেশিরভাগই একটি স্মৃতি মাপকাঠি হয়ে যায়। (এই উপসংহারটি সঠিকভাবে পাওয়ার পরে কুডোস যদিও: পার্থক্যটি "2.5x" না বলে "150ns" is আমার সন্দেহ হয় যে তিনি জীবিতদের জন্য এই জাতীয় কাজ করেন।)
আকর্ষণীয়ভাবে যথেষ্ট, সেটএ্যাক্সেসেবল (সত্য) স্থির করে, যা সুরক্ষা চেক এড়িয়ে চলেছে, তাতে ব্যয় 20% হ্রাস পেয়েছে।
সেট অ্যাক্সেসেবল (সত্য) ব্যতীত
new A(), 70 ns
A.class.newInstance(), 214 ns
new A(), 84 ns
A.class.newInstance(), 229 ns
সেট অ্যাক্সেসেবল (সত্য) সহ
new A(), 69 ns
A.class.newInstance(), 159 ns
new A(), 85 ns
A.class.newInstance(), 171 ns
1000000
?
setAccessible()
একাধিক যুক্তিযুক্ত পদ্ধতিগুলির ক্ষেত্রে সাধারণভাবে আরও অনেক বেশি পার্থক্য থাকতে পারে, তাই এটি সর্বদা বলা উচিত।
হ্যাঁ, এটি উল্লেখযোগ্যভাবে ধীর। আমরা এমন কিছু কোড চালিয়ে যাচ্ছিলাম যা এই কাজটি করেছিল এবং আমার কাছে এই মুহুর্তে মেট্রিকগুলি উপলব্ধ নেই, তবে শেষ ফলাফলটি ছিল যে প্রতিচ্ছবিটি ব্যবহার না করার জন্য আমাদের সেই কোডটি রিফেক্টর করতে হয়েছিল। ক্লাসটি কী তা আপনি যদি জানেন তবে সরাসরি কন্সট্রাক্টরকে সরাসরি কল করুন।
ক্লাসে ডাকা নতুন সংস্করণ () পরিবর্তে ক্লাস.ফোর্নাম ("মিসক্যা.এ") এর জন্য (যার জন্য একটি ক্লাস লুকের প্রয়োজন হবে, ফিলিস্টেমের উপর সম্ভবত ক্লাস পাথটি স্ক্যান করা প্রয়োজন) ডোরআরফ্ল্যাশনে () ওভারহেড। আমি ভাবছি যে পরিসংখ্যানগুলি দেখতে কেমন হবে যদি Class.forName ("Misc.A") কেবলমাত্র একবার লুপের বাইরে করা হয়, এটি লুপের প্রতিটি অনুরোধের জন্য করা উচিত নয়।
হ্যাঁ, সর্বদা প্রতিবিম্ব দ্বারা একটি অবজেক্ট তৈরি করা ধীর হবে কারণ জেভিএম সংকলনের সময় কোডটি অনুকূলিত করতে পারে না। আরও বিশদের জন্য সান / জাভা প্রতিবিম্ব টিউটোরিয়াল দেখুন See
এই সাধারণ পরীক্ষাটি দেখুন:
public class TestSpeed {
public static void main(String[] args) {
long startTime = System.nanoTime();
Object instance = new TestSpeed();
long endTime = System.nanoTime();
System.out.println(endTime - startTime + "ns");
startTime = System.nanoTime();
try {
Object reflectionInstance = Class.forName("TestSpeed").newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
endTime = System.nanoTime();
System.out.println(endTime - startTime + "ns");
}
}
Class.forName()
চেহারা () ইনস্ট্যান্সেশন (newInstance ()) থেকে পৃথক করা উচিত , কারণ এগুলি তাদের কর্মক্ষমতা বৈশিষ্ট্যের ক্ষেত্রে উল্লেখযোগ্যভাবে পরিবর্তিত হয় এবং আপনি মাঝে মাঝে একটি সুনির্দিষ্টভাবে নকশিত সিস্টেমে পুনরাবৃত্তিটি এড়াতে পারেন।
প্রায়শই আপনি অ্যাপাচি কমন্স বিয়ান ইউটিস বা প্রপার্টি ইউটিগুলি ব্যবহার করতে পারেন যা অন্তঃনির্ধারণ (মূলত তারা ক্লাসগুলি সম্পর্কে মেটা ডেটা ক্যাশে করে যাতে তাদের সর্বদা প্রতিচ্ছবি ব্যবহার করার প্রয়োজন হয় না)।
আমি মনে করি এটি টার্গেট পদ্ধতিটি কত হালকা / ভারী on যদি লক্ষ্য পদ্ধতিটি খুব হালকা হয় (উদাহরণস্বরূপ গিটার / সেটার), এটি 1 ~ 3 গুণ ধীর হতে পারে। যদি লক্ষ্য পদ্ধতিটি প্রায় 1 মিলিসেকেন্ড বা তারও বেশি সময় নেয় তবে পারফরম্যান্সটি খুব কাছাকাছি থাকবে। আমি জাভা 8 এবং প্রতিচ্ছবি দিয়ে যা পরীক্ষা করেছি তা এখানে :
public class ReflectionTest extends TestCase {
@Test
public void test_perf() {
Profiler.run(3, 100000, 3, "m_01 by refelct", () -> Reflection.on(X.class)._new().invoke("m_01")).printResult();
Profiler.run(3, 100000, 3, "m_01 direct call", () -> new X().m_01()).printResult();
Profiler.run(3, 100000, 3, "m_02 by refelct", () -> Reflection.on(X.class)._new().invoke("m_02")).printResult();
Profiler.run(3, 100000, 3, "m_02 direct call", () -> new X().m_02()).printResult();
Profiler.run(3, 100000, 3, "m_11 by refelct", () -> Reflection.on(X.class)._new().invoke("m_11")).printResult();
Profiler.run(3, 100000, 3, "m_11 direct call", () -> X.m_11()).printResult();
Profiler.run(3, 100000, 3, "m_12 by refelct", () -> Reflection.on(X.class)._new().invoke("m_12")).printResult();
Profiler.run(3, 100000, 3, "m_12 direct call", () -> X.m_12()).printResult();
}
public static class X {
public long m_01() {
return m_11();
}
public long m_02() {
return m_12();
}
public static long m_11() {
long sum = IntStream.range(0, 10).sum();
assertEquals(45, sum);
return sum;
}
public static long m_12() {
long sum = IntStream.range(0, 10000).sum();
assertEquals(49995000, sum);
return sum;
}
}
}
সম্পূর্ণ পরীক্ষার কোডটি গিটহাব: রিফ্লেকশন টেস্ট.জভাতে উপলব্ধ