2012-12-08 21 views
8

मैं ओरेकल बग डेटाबेस में एक आरएफई (वृद्धि के लिए अनुरोध) जमा करने पर विचार कर रहा हूं, जो स्ट्रिंग कॉन्सटेनेशन प्रदर्शन में काफी वृद्धि करना है। लेकिन इससे पहले कि मैं विशेषज्ञों की टिप्पणियों को सुनना चाहूंगा कि यह समझ में आता है या नहीं।क्या java.lang.String.concat में सुधार किया जा सकता है?

विचार इस तथ्य पर आधारित है कि मौजूदा स्ट्रिंग.कोनकैट (स्ट्रिंग) स्ट्रिंगबिल्डर की तुलना में 2 तारों पर दो गुना तेजी से काम करता है। समस्या यह है कि 3 या अधिक तारों को संयोजित करने की कोई विधि नहीं है। बाहरी तरीके ऐसा नहीं कर सकते क्योंकि String.concat एक पैकेज निजी कन्स्ट्रक्टर String(int offset, int count, char[] value) का उपयोग करता है जो चार सरणी की प्रतिलिपि नहीं करता है लेकिन इसे सीधे उपयोग करता है। यह उच्च String.concat प्रदर्शन सुनिश्चित करता है। एक ही पैकेज में होने के नाते स्ट्रिंगबिल्डर अभी भी इस कन्स्ट्रक्टर का उपयोग नहीं कर सकता है क्योंकि तब स्ट्रिंग की चार सरणी संशोधनों के लिए उजागर की जाएगी।

मैं स्ट्रिंग

public static String concat(String s1, String s2) 
public static String concat(String s1, String s2, String s3) 
public static String concat(String s1, String s2, String s3, String s4) 
public static String concat(String s1, String s2, String s3, String s4, String s5) 
public static String concat(String s1, String... array) 

नोट करने के लिए निम्न विधियों में जोड़ने के लिए सुझाव देते हैं: ओवरलोडिंग के इस प्रकार, EnumSet.of में प्रयोग किया जाता है दक्षता के लिए।

यह तरीकों में से एक के कार्यान्वयन है, दूसरों को, उसी तरह काम

public final class String { 
    private final char value[]; 
    private final int count; 
    private final int offset; 

    String(int offset, int count, char value[]) { 
     this.value = value; 
     this.offset = offset; 
     this.count = count; 
    } 

    public static String concat(String s1, String s2, String s3) { 
     char buf[] = new char[s1.count + s2.count + s3.count]; 
     System.arraycopy(s1.value, s1.offset, buf, 0, s1.count); 
     System.arraycopy(s2.value, s2.offset, buf, s1.count, s2.count); 
     System.arraycopy(s3.value, s3.offset, buf, s1.count + s2.count, s3.count); 
     return new String(0, buf.length, buf); 
    } 

इसके अलावा के बाद इन तरीकों स्ट्रिंग, जावा के लिए जोड़ रहे

String s = s1 + s2 + s3; 

के लिए कर सकेंगे कुशल वर्तमान अक्षम

के

String s = String.concat(s1, s2, s3); 

बजाय का निर्माण

String s = (new StringBuilder(String.valueOf(s1))).append(s2).append(s3).toString(); 

अद्यतन प्रदर्शन परीक्षण। मैंने इसे अपने नोटबुक इंटेल सेलेरॉन 925 पर चलाया, 3 स्ट्रिंग्स का संयोजन, मेरी स्ट्रिंग 2 कक्षा वास्तव में यह बताती है कि यह वास्तविक java.lang.String में कैसे होगा। स्ट्रिंग लम्बाई चुने जाते हैं ताकि स्ट्रिंगबिल्डर को सबसे प्रतिकूल परिस्थितियों में रखा जा सके, जब वह प्रत्येक परिशिष्ट पर अपनी आंतरिक बफर क्षमता का विस्तार करने की आवश्यकता होती है, जबकि कॉन्सट हमेशा एक बार [] को बनाता है।

public class String2 { 
    private final char value[]; 
    private final int count; 
    private final int offset; 

    String2(String s) { 
     value = s.toCharArray(); 
     offset = 0; 
     count = value.length; 
    } 

    String2(int offset, int count, char value[]) { 
     this.value = value; 
     this.offset = offset; 
     this.count = count; 
    } 

    public static String2 concat(String2 s1, String2 s2, String2 s3) { 
     char buf[] = new char[s1.count + s2.count + s3.count]; 
     System.arraycopy(s1.value, s1.offset, buf, 0, s1.count); 
     System.arraycopy(s2.value, s2.offset, buf, s1.count, s2.count); 
     System.arraycopy(s3.value, s3.offset, buf, s1.count + s2.count, s3.count); 
     return new String2(0, buf.length, buf); 
    } 

    public static void main(String[] args) { 
     String s1 = "1"; 
     String s2 = "11111111111111111"; 
     String s3 = "11111111111111111111111111111111111111111"; 
     String2 s21 = new String2(s1); 
     String2 s22 = new String2(s2); 
     String2 s23 = new String2(s3); 
     long t0 = System.currentTimeMillis(); 
     for (int i = 0; i < 1000000; i++) { 
      String2 s = String2.concat(s21, s22, s23); 
//   String s = new StringBuilder(s1).append(s2).append(s3).toString(); 
     } 
     System.out.println(System.currentTimeMillis() - t0); 
    } 
} 

1.000.000 पर पुनरावृत्तियों परिणाम हैं:

version 1 = ~200 ms 
version 2 = ~400 ms 
+0

स्ट्रिंग बफर बहुत अधिक तेजी से है कि आप –

उत्तर

7

तथ्य यह है कि उपयोग के मामलों के लिए एक स्ट्रिंग कॉन्सटेनेशन अभिव्यक्ति मामलों का प्रदर्शन सामान्य नहीं है। ज्यादातर मामलों में जहां स्ट्रिंग कॉन्सटेनेशन द्वारा प्रदर्शन बाध्य होता है, यह एक लूप में होता है, चरण के अंत उत्पाद चरण का निर्माण करता है, और उस संदर्भ में mutable StringBuilder एक स्पष्ट विजेता है।यही कारण है कि मुझे ऐसे प्रस्ताव के लिए बहुत परिप्रेक्ष्य दिखाई नहीं देता है जो मौलिक String कक्षा में हस्तक्षेप करके अल्पसंख्यक चिंता को अनुकूलित करता है।

import com.google.caliper.Runner; 
import com.google.caliper.SimpleBenchmark; 

public class Performance extends SimpleBenchmark 
{ 
    final Random rnd = new Random(); 
    final String as1 = "aoeuaoeuaoeu", as2 = "snthsnthnsth", as3 = "3453409345"; 
    final char[] c1 = as1.toCharArray(), c2 = as2.toCharArray(), c3 = as3.toCharArray(); 

    public static char[] concat(char[] s1, char[] s2, char[] s3) { 
    char buf[] = new char[s1.length + s2.length + s3.length]; 
    System.arraycopy(s1, 0, buf, 0, s1.length); 
    System.arraycopy(s2, 0, buf, s1.length, s2.length); 
    System.arraycopy(s3, 0, buf, s1.length + s2.length, s3.length); 
    return buf; 
    } 

    public static String build(String s1, String s2, String s3) { 
    final StringBuilder b = new StringBuilder(s1.length() + s2.length() + s3.length()); 
    b.append(s1).append(s2).append(s3); 
    return b.toString(); 
    } 

    public static String plus(String s1, String s2, String s3) { 
    return s1 + s2 + s3; 
    } 

    public int timeConcat(int reps) { 
    int tot = rnd.nextInt(); 
    for (int i = 0; i < reps; i++) tot += concat(c1, c2, c3).length; 
    return tot; 
    } 

    public int timeBuild(int reps) { 
    int tot = rnd.nextInt(); 
    for (int i = 0; i < reps; i++) tot += build(as1, as2, as3).length(); 
    return tot; 
    } 

    public int timePlus(int reps) { 
    int tot = rnd.nextInt(); 
    for (int i = 0; i < reps; i++) tot += plus(as1, as2, as3).length(); 
    return tot; 
    } 

    public static void main(String... args) { 
    Runner.main(Performance.class, args); 
    } 
} 

परिणाम::

0% Scenario{vm=java, trial=0, benchmark=Concat} 65.81 ns; σ=2.56 ns @ 10 trials 
33% Scenario{vm=java, trial=0, benchmark=Build} 102.94 ns; σ=2.27 ns @ 10 trials 
67% Scenario{vm=java, trial=0, benchmark=Plus} 160.14 ns; σ=2.94 ns @ 10 trials 

benchmark ns linear runtime 
    Concat 65.8 ============ 
    Build 102.9 =================== 
    Plus 160.1 ============================== 
+1

को अस्वीकार करना असंभव था। मेरी पोस्ट में कुछ बेंचमार्क गुणा और जोड़ देगा। –

4

आप उन्हें आप को गंभीरता से ले जाना चाहते हैं, तो आप पूरी तरह से लागू करने, परीक्षण और अच्छी तरह से अपनी प्रस्तावित परिवर्तन बेंचमार्किंग की कड़ी मेहनत करने की जरूरत है। और एक पूर्ण कार्यान्वयन में आपके विधियों का उपयोग करने के लिए बाइटकोड को उत्सर्जित करने के लिए जावा कंपाइलर में परिवर्तन शामिल होंगे।

परिणाम लिखें, और फिर OpenJDK 7 या 8 के लिए एक पैच के रूप में कोड में परिवर्तन प्रस्तुत

मेरे धारणा है कि जावा डेवलपर्स इस तरह अनुकूलन के लिए सट्टा विचारों आज़माने के लिए संसाधन नहीं है एक। बेंचमार्किंग परिणाम और कोड पैच के बिना एक आरएफई ध्यान देने की संभावना नहीं है ...

+0

अधिकार हासिल करना चाहते हो सकता है, मैं पहले से ही बग डेटाबेस के लिए कुछ कीड़े (या क्या मैं कीड़े होने के लिए लगता है) जमा करने की कोशिश की है। अभी तक केवल एक ही प्रयास, डेक की जावाडोक बग, http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7178639 सफल रहा है। –

1

यह हमेशा उनसे ठीक है, चिंता मत करो लेकिन वैसे भी, जहाँ तक प्रदर्शन की तुलना के रूप में, अपने दृष्टिकोण एक महत्वपूर्ण बढ़त हासिल है है।

मेरे पास इतने सारे अधिभारित संस्करण नहीं होंगे। EnumSet में बचत महत्वपूर्ण हो सकती है; स्ट्रिंग में ऐसा नहीं है।

वास्तव में मुझे लगता है कि एक स्थिर विधि आर्ग के किसी भी संख्या के लिए अनुमति देता बेहतर

public static String join(String... strings) 

है के बाद से आर्ग की संख्या संकलन समय पर अज्ञात हो सकता है।

+0

एकाधिक ओवरलोडेड विधियों का विचार जोश ब्लोच से संबंधित है, यह "एआर से कम अगर एआर आवंटन की लागत से बचाता है"। अर्थात। शामिल हों ("1", "2") प्रभावी रूप से शामिल है (नया स्ट्रिंग [] {"1", "2"}), एक अतिरिक्त सरणी बनाई गई है। चूंकि पूरा विषय प्रदर्शन के बारे में है, इसलिए जोश ब्लॉक का मुहावर प्रासंगिक प्रतीत होता है। –

+0

Enumset में, तर्क सरल परमाणु हैं। स्ट्रिंग में, तर्कों की प्रतिलिपि बनाई जानी चाहिए, इसलिए vararg का ऊपरी भाग अपेक्षाकृत नगण्य है। – irreputable