क्या कोई प्रदर्शन एक तरह से या किसी अन्य लाभ है? क्या यह संकलक / वीएम विशिष्ट है? मैं हॉटस्पॉट का उपयोग कर रहा हूं।
क्या कोई प्रदर्शन एक तरह से या किसी अन्य लाभ है? क्या यह संकलक / वीएम विशिष्ट है? मैं हॉटस्पॉट का उपयोग कर रहा हूं।
जवाबों:
पहला: आपको प्रदर्शन के आधार पर स्थिर बनाम गैर-स्थिर का विकल्प नहीं बनाना चाहिए।
दूसरा: व्यवहार में, इससे कोई फर्क नहीं पड़ेगा। हॉटस्पॉट उन तरीकों से अनुकूलन करने का विकल्प चुन सकता है जो एक विधि के लिए स्थिर कॉल तेजी से करते हैं, दूसरे के लिए गैर-स्थिर कॉल तेजी से।
तीसरा: स्थिर बनाम गैर स्थिर आसपास के मिथक के ज्यादा या तो पर बहुत पुरानी JVMs (जो अनुकूलन कि हॉटस्पॉट करता है के पास कहीं भी नहीं किया हो) आधारित होते हैं, या सी के बारे में कुछ याद सामान्य ज्ञान ++ (जो एक गतिशील कॉल का उपयोग करता है में एक अधिक स्मृति का उपयोग एक स्थिर कॉल की तुलना में)।
चार साल बाद...
ठीक है, इस प्रश्न को एक बार और हमेशा के लिए निपटाने की उम्मीद में, मैंने एक बेंचमार्क लिखा है जिसमें दिखाया गया है कि विभिन्न प्रकार की कॉल (आभासी, गैर-आभासी, स्थिर) एक दूसरे से तुलना कैसे करती हैं।
मैंने इसे विचारधारा पर चलाया , और यही मुझे मिला:
(पुनरावृत्तियों की बड़ी संख्या बेहतर है।)
Success time: 3.12 memory: 320576 signal:0
Name | Iterations
VirtualTest | 128009996
NonVirtualTest | 301765679
StaticTest | 352298601
Done.
जैसा कि अपेक्षित था, वर्चुअल मेथड कॉल्स सबसे धीमी हैं, नॉन-वर्चुअल मैथड कॉल्स तेज़ हैं, और स्टैटिक मेथड कॉल्स और भी तेज़ हैं।
मुझे उम्मीद नहीं थी कि मतभेद इतने स्पष्ट होंगे: वर्चुअल मेथड कॉल्स को गैर-वर्चुअल मेथड कॉल्स की आधी से कम गति से चलाने के लिए मापा गया था, जो बदले में स्टैटिक कॉल्स की तुलना में पूरे 15% धीमी गति से चलने के लिए मापा गया था । यही माप दिखाता है; वास्तविक अंतर वास्तव में थोड़ा अधिक स्पष्ट होना चाहिए, क्योंकि प्रत्येक आभासी, गैर-आभासी और स्थिर विधि कॉल के लिए, मेरे बेंचमार्किंग कोड में एक पूर्णांक चर को बढ़ाने के लिए एक अतिरिक्त निरंतर ओवरहेड होता है, जो बूलियन चर की जाँच करता है, और यदि सही नहीं है तो लूपिंग।
मुझे लगता है कि परिणाम सीपीयू से सीपीयू और जेवीएम से जेवीएम तक अलग-अलग होंगे, इसलिए इसे आजमाएं और देखें कि आप क्या कर रहे हैं:
import java.io.*;
class StaticVsInstanceBenchmark
{
public static void main( String[] args ) throws Exception
{
StaticVsInstanceBenchmark program = new StaticVsInstanceBenchmark();
program.run();
}
static final int DURATION = 1000;
public void run() throws Exception
{
doBenchmark( new VirtualTest( new ClassWithVirtualMethod() ),
new NonVirtualTest( new ClassWithNonVirtualMethod() ),
new StaticTest() );
}
void doBenchmark( Test... tests ) throws Exception
{
System.out.println( " Name | Iterations" );
doBenchmark2( devNull, 1, tests ); //warmup
doBenchmark2( System.out, DURATION, tests );
System.out.println( "Done." );
}
void doBenchmark2( PrintStream printStream, int duration, Test[] tests ) throws Exception
{
for( Test test : tests )
{
long iterations = runTest( duration, test );
printStream.printf( "%15s | %10d\n", test.getClass().getSimpleName(), iterations );
}
}
long runTest( int duration, Test test ) throws Exception
{
test.terminate = false;
test.count = 0;
Thread thread = new Thread( test );
thread.start();
Thread.sleep( duration );
test.terminate = true;
thread.join();
return test.count;
}
static abstract class Test implements Runnable
{
boolean terminate = false;
long count = 0;
}
static class ClassWithStaticStuff
{
static int staticDummy;
static void staticMethod() { staticDummy++; }
}
static class StaticTest extends Test
{
@Override
public void run()
{
for( count = 0; !terminate; count++ )
{
ClassWithStaticStuff.staticMethod();
}
}
}
static class ClassWithVirtualMethod implements Runnable
{
int instanceDummy;
@Override public void run() { instanceDummy++; }
}
static class VirtualTest extends Test
{
final Runnable runnable;
VirtualTest( Runnable runnable )
{
this.runnable = runnable;
}
@Override
public void run()
{
for( count = 0; !terminate; count++ )
{
runnable.run();
}
}
}
static class ClassWithNonVirtualMethod
{
int instanceDummy;
final void nonVirtualMethod() { instanceDummy++; }
}
static class NonVirtualTest extends Test
{
final ClassWithNonVirtualMethod objectWithNonVirtualMethod;
NonVirtualTest( ClassWithNonVirtualMethod objectWithNonVirtualMethod )
{
this.objectWithNonVirtualMethod = objectWithNonVirtualMethod;
}
@Override
public void run()
{
for( count = 0; !terminate; count++ )
{
objectWithNonVirtualMethod.nonVirtualMethod();
}
}
}
static final PrintStream devNull = new PrintStream( new OutputStream()
{
public void write(int b) {}
} );
}
यह ध्यान देने योग्य है कि यह प्रदर्शन अंतर केवल उस कोड पर लागू होता है जो पैरामीटर रहित विधियों को लागू करने के अलावा और कुछ नहीं करता है। जो भी कोड आपके पास इनवोकेशन के बीच है वह अंतरों को कम करेगा, और इसमें पैरामीटर पासिंग शामिल है। दरअसल, स्थैतिक और गैर-व्यावसायिक कॉल के बीच 15% अंतर को संभवतः इस तथ्य से पूर्ण रूप से समझाया गया है कि this
सूचक को स्थैतिक विधि से पारित नहीं होना है। तो, यह केवल कॉल की एक छोटी सी राशि ले जाएगा जिसमें विभिन्न प्रकार के कॉल के बीच के अंतर के लिए तुच्छ सामान होता है, जो कि शुद्ध प्रभाव नहीं होने के बिंदु पर पतला होता है।
इसके अलावा, वर्चुअल मेथड कॉल्स एक कारण से मौजूद हैं; उनके पास सेवा करने का उद्देश्य नहीं है, और उन्हें अंतर्निहित हार्डवेयर द्वारा प्रदान किए गए सबसे कुशल साधनों का उपयोग करके कार्यान्वित किया जाता है। (सीपीयू इंस्ट्रक्शन सेट।) यदि, आपकी इच्छा से उन्हें अनैच्छिक या स्थिर कॉल के साथ समाप्त करने की इच्छा रखते हैं, तो आप अंत में उनकी कार्यक्षमता का अनुकरण करने के लिए अतिरिक्त कोड के एक कोटे के रूप में जोड़ना चाहते हैं, तो आपका परिणामी नेट ओवरहेड बाध्य है कम नहीं, लेकिन अधिक होना। संभवतः, बहुत, बहुत, अथाह, बहुत कुछ।
VirtualTest | 488846733 -- NonVirtualTest | 480530022 -- StaticTest | 484353198
मेरे OpenJDK इंस्टॉलेशन पर। FTR: यह और भी सही है अगर मैं final
संशोधक को हटा दूं। Btw। मुझे terminate
मैदान बनाना था volatile
, वरना टेस्ट खत्म नहीं होता।
VirtualTest | 12451872 -- NonVirtualTest | 12089542 -- StaticTest | 8181170
। इतना ही नहीं मेरी नोटबुक पर OpenJDK 40x अधिक पुनरावृत्तियों को करने का प्रबंधन करता है, स्थैतिक परीक्षण में हमेशा लगभग 30% कम थ्रूपुट होता है। यह एक एआरटी विशिष्ट घटना हो सकती है, क्योंकि मुझे एंड्रॉइड 4.4 टैबलेट पर एक अपेक्षित परिणाम मिलता है:VirtualTest | 138183740 -- NonVirtualTest | 142268636 -- StaticTest | 161388933
ठीक है, स्थैतिक कॉल को ओवरराइड नहीं किया जा सकता है (इसलिए हमेशा इनलाइनिंग के लिए उम्मीदवार हैं), और किसी भी अशक्तता जांच की आवश्यकता नहीं है। हॉटस्पॉट उदाहरण के तरीकों के लिए शांत अनुकूलन का एक गुच्छा करता है जो इन लाभों को अच्छी तरह से नकार सकते हैं, लेकिन वे संभावित कारण हैं कि एक स्थिर कॉल तेज क्यों हो सकती है।
हालाँकि, यह आपके डिज़ाइन - कोड को सबसे अधिक पठनीय, प्राकृतिक तरीके से प्रभावित नहीं करना चाहिए - और इस तरह के सूक्ष्म-अनुकूलन के बारे में चिंता करें यदि आपके पास सिर्फ कारण है (जो आप लगभग कभी नहीं करेंगे)।
यह संकलक / वीएम विशिष्ट है।
इसलिए यह शायद तब तक परेशान करने लायक नहीं है जब तक कि आपने इसे अपने आवेदन में वास्तव में महत्वपूर्ण प्रदर्शन समस्या के रूप में नहीं पहचाना हो। समयपूर्व अनुकूलन सभी बुराई आदि की जड़ है ...
हालाँकि मैंने देखा है कि यह अनुकूलन निम्नलिखित स्थिति में पर्याप्त प्रदर्शन बढ़ाता है:
यदि उपरोक्त आप पर लागू होता है, तो यह परीक्षण के लायक हो सकता है।
स्थैतिक विधि का उपयोग करने के लिए एक अन्य अच्छा (और संभावित रूप से और भी महत्वपूर्ण है!) कारण है - यदि विधि में वास्तव में स्थैतिक शब्दार्थ है (अर्थात तार्किक रूप से वर्ग के दिए गए उदाहरण से जुड़ा नहीं है) तो यह इसे स्थिर बनाने के लिए समझ में आता है इस तथ्य को प्रतिबिंबित करने के लिए। अनुभवी जावा प्रोग्रामर तब स्थिर संशोधक को नोटिस करेंगे और तुरंत सोचेंगे "अहा! यह विधि स्थिर है इसलिए इसे उदाहरण की आवश्यकता नहीं है और संभवतः उदाहरण के लिए विशिष्ट स्थिति में हेरफेर नहीं करता है"। तो आपने प्रभावी तरीके से स्थैतिक प्रकृति का संचार किया होगा ...।
जैसा कि पिछले पोस्टरों ने कहा है: यह एक समयपूर्व अनुकूलन की तरह लगता है।
हालांकि, एक अंतर है (इस तथ्य से एक हिस्सा है कि गैर-स्थैतिक इनवोकेशन को ऑपरेंड स्टैक पर एक कैली-ऑब्जेक्ट के अतिरिक्त पुश की आवश्यकता होती है):
चूंकि स्थैतिक विधियों को ओवरराइड नहीं किया जा सकता है, इसलिए स्थैतिक विधि कॉल के लिए रनटाइम में कोई वर्चुअल लुकअप नहीं होगा । इसके परिणामस्वरूप कुछ परिस्थितियों में एक अंतर दिखाई दे सकता है।
बाइट-कोड स्तर पर अंतर यह है कि एक गैर-स्थैतिक विधि कॉल के माध्यम से किया जाता है INVOKEVIRTUAL
, INVOKEINTERFACE
या INVOKESPECIAL
जबकि एक स्थिर विधि कॉल के माध्यम से किया जाता है INVOKESTATIC
।
invokespecial
क्योंकि यह आभासी नहीं है।
यह अविश्वसनीय रूप से संभावना नहीं है कि स्थिर बनाम गैर-स्थिर कॉल के प्रदर्शन में कोई अंतर आपके एप्लिकेशन में फर्क कर रहा है। याद रखें कि "समयपूर्व अनुकूलन सभी बुराई की जड़ है"।
7 साल बाद ...
मेरे पास माइक नाकिस के परिणामों में बहुत बड़ा विश्वास नहीं है क्योंकि वे हॉटस्पॉट के अनुकूलन से संबंधित कुछ सामान्य मुद्दों को संबोधित नहीं करते हैं। मैंने JMH का उपयोग करते हुए बेंचमार्क इंस्ट्रूमेंट किया है और एक उदाहरण विधि का ओवरहेड पाया है जो कि मेरी मशीन बनाम स्टेटिक कॉल पर लगभग 0.75% है। यह देखते हुए कि कम ओवरहेड मुझे लगता है कि सबसे विलंबता संवेदनशील संचालन को छोड़कर यह एक आवेदन डिजाइन में सबसे बड़ी चिंता नहीं है। मेरे जेएमएच बेंचमार्क से सारांश परिणाम निम्नानुसार हैं;
java -jar target/benchmark.jar
# -- snip --
Benchmark Mode Cnt Score Error Units
MyBenchmark.testInstanceMethod thrpt 200 414036562.933 ± 2198178.163 ops/s
MyBenchmark.testStaticMethod thrpt 200 417194553.496 ± 1055872.594 ops/s
आप यहाँ Github पर कोड देख सकते हैं;
https://github.com/nfisher/svsi
बेंचमार्क अपने आप में बहुत सरल है लेकिन मृत कोड उन्मूलन और निरंतर तह को कम करना है। संभवतः अन्य अनुकूलन हैं जो मैंने याद किया / अनदेखा किया है और ये परिणाम जेवीएम रिलीज़ और ओएस के अनुसार भिन्न हो सकते हैं।
package ca.junctionbox.svsi;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.Blackhole;
class InstanceSum {
public int sum(final int a, final int b) {
return a + b;
}
}
class StaticSum {
public static int sum(final int a, final int b) {
return a + b;
}
}
public class MyBenchmark {
private static final InstanceSum impl = new InstanceSum();
@State(Scope.Thread)
public static class Input {
public int a = 1;
public int b = 2;
}
@Benchmark
public void testStaticMethod(Input i, Blackhole blackhole) {
int sum = StaticSum.sum(i.a, i.b);
blackhole.consume(sum);
}
@Benchmark
public void testInstanceMethod(Input i, Blackhole blackhole) {
int sum = impl.sum(i.a, i.b);
blackhole.consume(sum);
}
}
ops/s
मुख्य रूप से एआरटी वातावरण (उदाहरण के उपयोग, कम। फ़ाइल फ़ाइल आकार आदि) के अलावा अन्य मैट्रिक्स पर हो सकते हैं । क्या आप किसी भी अपेक्षाकृत सरल उपकरण / तरीके के बारे में जानते हैं जो इन अन्य मैट्रिक्स को बेंचमार्क करने का प्रयास कर सकता है?
निर्णय के लिए यदि कोई विधि स्थिर होनी चाहिए, तो प्रदर्शन का पहलू अप्रासंगिक होना चाहिए। यदि आपको प्रदर्शन की समस्या है, तो बहुत सारे तरीकों को स्थिर बनाने से दिन को बचाने की जरूरत नहीं है। कहा कि, स्थिर तरीके लगभग निश्चित रूप से किसी भी उदाहरण विधि की तुलना में धीमी नहीं हैं , ज्यादातर मामलों में तेजी से ।
1.) स्टेटिक तरीके बहुरूपी नहीं हैं, इसलिए जेवीएम के पास निष्पादन के लिए वास्तविक कोड खोजने के लिए कम निर्णय हैं। यह हॉटस्पॉट की आयु में एक म्यूट पॉइंट है, क्योंकि हॉटस्पॉट इंस्टेंस विधि कॉल को अनुकूलित करेगा जिसमें केवल एक कार्यान्वयन साइट है, इसलिए वे समान प्रदर्शन करेंगे।
2.) एक और सूक्ष्म अंतर यह है कि स्थिर तरीकों में स्पष्ट रूप से "यह" संदर्भ नहीं है। इसके परिणामस्वरूप एक स्टैक फ्रेम एक स्लॉट उसी हस्ताक्षर विधि और शरीर के साथ एक इंस्टेंस विधि से छोटा होता है ("यह" बाइटकोड स्तर पर स्थानीय चर के स्लॉट 0 में रखा जाता है, जबकि स्थैतिक तरीकों के लिए स्लॉट 0 का उपयोग पहली बार किया जाता है) विधि का पैरामीटर)।
इसमें अंतर हो सकता है, और यह किसी भी विशेष कोड कोड के लिए रास्ता हो सकता है, और यह JVM के मामूली रिलीज के साथ बदल सकता है।
यह निश्चित रूप से 97% छोटी क्षमता का हिस्सा है जिसे आपको भूल जाना चाहिए ।
TableView
लाखों अभिलेखों के लिए एक खोज ।
सिद्धांत रूप में, कम महंगा है।
यदि आप ऑब्जेक्ट का एक उदाहरण बनाते हैं, तो भी स्थैतिक आरंभीकरण होने जा रहा है, जबकि स्थैतिक विधियाँ सामान्य रूप से किसी रचना में किया गया इनिशियलाइज़ेशन नहीं करेगा।
हालाँकि, मैंने इसका परीक्षण नहीं किया है।
जॉन नोट्स के रूप में, स्थैतिक तरीकों को ओवरराइड नहीं किया जा सकता है, इसलिए बस एक स्थिर विधि को लागू करना हो सकता है - एक पर्याप्त भोले जावा रनटाइम पर - एक इंस्टेंस विधि को लागू करने की तुलना में तेज़ ।
लेकिन फिर, यहां तक कि आप उस बिंदु पर हैं जहां आप अपने डिजाइन को कुछ नैनोसेकंड को बचाने के लिए गड़बड़ करने के बारे में परवाह करते हैं, कि बस एक और सवाल उठता है: क्या आपको खुद को ओवरराइड करने की विधि की आवश्यकता होगी? यदि आप एक नैनो विधि से इधर-उधर जाने के लिए एक स्थिर विधि में एक इंस्टेंस विधि बनाने के लिए अपना कोड बदलते हैं, और फिर अपने चारों ओर अपना स्वयं का डिस्पैचर लागू करते हैं, तो आपका निश्चित रूप से निर्मित की तुलना में कम कुशल होने वाला है आपके जावा रनटाइम में पहले से ही।
मैं यहां अन्य महान उत्तरों में जोड़ना चाहूंगा कि यह आपके प्रवाह पर भी निर्भर करता है, उदाहरण के लिए:
Public class MyDao {
private String sql = "select * from MY_ITEM";
public List<MyItem> getAllItems() {
springJdbcTemplate.query(sql, new MyRowMapper());
};
};
ध्यान दें कि आप प्रत्येक कॉल पर एक नया MyRowMapper ऑब्जेक्ट बनाते हैं।
इसके बजाय, मैं यहां एक स्थिर क्षेत्र का उपयोग करने का सुझाव देता हूं।
Public class MyDao {
private static RowMapper myRowMapper = new MyRowMapper();
private String sql = "select * from MY_ITEM";
public List<MyItem> getAllItems() {
springJdbcTemplate.query(sql, myRowMapper);
};
};