2008-10-21 4 views
8

मैं चार बाइट के लिए एक हेक्साडेसिमल मान (ऊपरी या निचली मामले में) का प्रतिनिधित्व करने से कन्वर्ट करने के लिए, की तरहप्रदर्शन प्रश्न: हेक्साडेसिमल चार को जावा में अपने मूल्य मान में परिवर्तित करने का सबसे तेज़ तरीका?

'0'->0, '1' -> 1, 'A' -> 10, 'a' -> 10, 'f' -> 15 etc... 

मैं बहुत अक्सर इस विधि कॉल करेंगे चाहते हैं, इसलिए प्रदर्शन महत्वपूर्ण है। क्या मूल्य प्राप्त करने के लिए पूर्व-प्रारंभिक HashMap<Character,Byte> का उपयोग करने से कहीं अधिक तेज़ तरीका है?

उत्तर

ऐसा लगता है जैसे कि यह एक स्विच मामले का उपयोग कर और जॉन स्कीट की प्रत्यक्ष कंप्यूटिंग समाधान के बीच एक tossup है - स्विच मामले समाधान हालांकि कभी तो थोड़ा बाहर भेजने के लिए, लगता है। ग्रेग की सरणी विधि जीत जाती है। विभिन्न विधियों के 200,000,000 रनों के लिए प्रदर्शन परिणाम (एमएस में) हैं:

Character.getNumericValue: 
8360 

Character.digit: 
8453 

HashMap<Character,Byte>: 
15109 

Greg's Array Method: 
6656 

JonSkeet's Direct Method: 
7344 

Switch: 
7281 

धन्यवाद दोस्तों!

बेंचमार्क विधि कोड

यहाँ फिर जाना, JonSkeet, तो आप पुराने प्रतियोगी। ;-)

public class ScratchPad { 

    private static final int NUMBER_OF_RUNS = 200000000; 

    static byte res; 

    static HashMap<Character, Byte> map = new HashMap<Character, Byte>() {{ 
     put(Character.valueOf('0'), Byte.valueOf((byte)0)); 
     put(Character.valueOf('1'), Byte.valueOf((byte)1)); 
     put(Character.valueOf('2'), Byte.valueOf((byte)2)); 
     put(Character.valueOf('3'), Byte.valueOf((byte)3)); 
     put(Character.valueOf('4'), Byte.valueOf((byte)4)); 
     put(Character.valueOf('5'), Byte.valueOf((byte)5)); 
     put(Character.valueOf('6'), Byte.valueOf((byte)6)); 
     put(Character.valueOf('7'), Byte.valueOf((byte)7)); 
     put(Character.valueOf('8'), Byte.valueOf((byte)8)); 
     put(Character.valueOf('9'), Byte.valueOf((byte)9)); 
     put(Character.valueOf('a'), Byte.valueOf((byte)10)); 
     put(Character.valueOf('b'), Byte.valueOf((byte)11)); 
     put(Character.valueOf('c'), Byte.valueOf((byte)12)); 
     put(Character.valueOf('d'), Byte.valueOf((byte)13)); 
     put(Character.valueOf('e'), Byte.valueOf((byte)14)); 
     put(Character.valueOf('f'), Byte.valueOf((byte)15)); 
     put(Character.valueOf('A'), Byte.valueOf((byte)10)); 
     put(Character.valueOf('B'), Byte.valueOf((byte)11)); 
     put(Character.valueOf('C'), Byte.valueOf((byte)12)); 
     put(Character.valueOf('D'), Byte.valueOf((byte)13)); 
     put(Character.valueOf('E'), Byte.valueOf((byte)14)); 
     put(Character.valueOf('F'), Byte.valueOf((byte)15)); 
    }}; 
    static int[] charValues = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, 
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,10, 11, 12, 13,14,15}; 
    static char[] cs = new char[]{'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','A','B','C','D','E','F'}; 

    public static void main(String args[]) throws Exception { 
     long time = System.currentTimeMillis(); 
     for(int i = 0; i < NUMBER_OF_RUNS; i++) { 
      res = getNumericValue(i); 
     } 
     System.out.println("Character.getNumericValue:"); 
     System.out.println(System.currentTimeMillis()-time); 
     time = System.currentTimeMillis(); 
     for(int i = 0; i < NUMBER_OF_RUNS; i++) { 
      res = getDigit(i); 
     } 
     System.out.println("Character.digit:"); 
     System.out.println(System.currentTimeMillis()-time); 
     time = System.currentTimeMillis(); 
     for(int i = 0; i < NUMBER_OF_RUNS; i++) { 
      try { 
       res = getValueFromArray(i); 
      } catch (IllegalArgumentException e) { 
      } 
     } 
     System.out.println("Array:"); 
     System.out.println(System.currentTimeMillis()-time); 
     time = System.currentTimeMillis(); 
     for(int i = 0; i < NUMBER_OF_RUNS; i++) { 
      res = getValueFromHashMap(i); 
     } 
     System.out.println("HashMap<Character,Byte>:"); 
     System.out.println(System.currentTimeMillis()-time); 
     time = System.currentTimeMillis(); 
     for(int i = 0; i < NUMBER_OF_RUNS; i++) { 
      char c = cs[i%cs.length]; 
      res = getValueFromComputeMethod(c);   
     } 
     System.out.println("JonSkeet's Direct Method:"); 
     System.out.println(System.currentTimeMillis()-time); 
     time = System.currentTimeMillis(); 
     for(int i = 0; i < NUMBER_OF_RUNS; i++) { 
      res = getValueFromSwitch(i); 

     } 
     System.out.println("Switch:"); 
     System.out.println(System.currentTimeMillis()-time); 
    } 

    private static byte getValueFromSwitch(int i) { 
     byte res; 
     char ch = cs[i%cs.length]; 
     switch(ch) { 
      case '0': 
       res = 0; 
       break; 
      case '1': 
       res = 1; 
       break; 
      case '2': 
       res = 2; 
       break; 
      case '3': 
       res = 3; 
       break; 
      case '4': 
       res = 4; 
       break; 
      case '5': 
       res = 5; 
       break; 
      case '6': 
       res = 6; 
       break; 
      case '7': 
       res = 7; 
       break; 
      case '8': 
       res = 8; 
       break; 
      case '9': 
       res = 9; 
       break; 
      case 'a': 
      case 'A': 
       res = 10; 
       break; 
      case 'b': 
      case 'B':  
       res = 11; 
       break; 
      case 'c': 
      case 'C':  
       res = 12; 
       break; 
      case 'd': 
      case 'D':  
       res = 13; 
       break; 
      case 'e': 
      case 'E':  
       res = 14; 
       break; 
      case 'f': 
      case 'F':  
       res = 15; 
       break; 
      default: 
       throw new RuntimeException("unknown hex character: " + ch); 
     } 
     return res; 
    } 

    private static byte getValueFromComputeMethod(char c) { 
     byte result = 0; 
     if (c >= '0' && c <= '9') 
     { 
      result = (byte)(c - '0'); 
     } 
     if (c >= 'a' && c <= 'f') 
     { 
      result = (byte)(c - 'a' + 10); 
     } 
     if (c >= 'A' && c <= 'F') 
     { 
      result = (byte)(c - 'A' + 10); 
     } 
     return result; 
    } 

    private static byte getValueFromHashMap(int i) { 
     return map.get(Character.valueOf(cs[i%cs.length])).byteValue(); 
    } 

    private static byte getValueFromArray(int i) { 
     char c = cs[i%cs.length]; 
     if (c < '0' || c > 'f') { 
      throw new IllegalArgumentException(); 
     } 
     byte result = (byte)charValues[c-'0']; 
     if (res < 0) { 
      throw new IllegalArgumentException(); 
     } 
     return result; 
    } 

    private static byte getDigit(int i) { 
     return (byte)Character.digit(cs[i%cs.length], 16); 
    } 

    private static byte getNumericValue(int i) { 
     return (byte)Character.getNumericValue(cs[i%cs.length]); 
    } 

} 
+0

पुस्तकालय कार्यों का उपयोग कर कोई प्रदर्शन समस्याएं थीं? यदि कोई नहीं है, तो Character.digit (char, radix) पर्याप्त होना चाहिए। – questzen

+0

इस बेंचमार्क के साथ विचार करने की एक बात यह है कि सरणी अच्छी तरह से काम करेगी * जबकि यह कैश में है * - यह बहुत अच्छा है यदि आप एक ही बार में कई पुनरावृत्तियों कर रहे हैं। यदि आपको मुख्य मेमोरी से प्राप्त होने वाली सरणी की प्रतीक्षा करनी है, हालांकि, पूरी तरह से कम्प्यूटेशनल दृष्टिकोण तेज होगा। –

+0

दूसरे शब्दों में: "यह निर्भर करता है" :) –

उत्तर

15

एक पूर्वनिर्धारित सरणी हैश मैप से तेज होगी। कुछ इस तरह:

int CharValues['f'-'0'+1] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, ... -1, 10, 11, 12, ...}; 

if (c < '0' || c > 'f') { 
    throw new IllegalArgumentException(); 
} 
int n = CharValues[c-'0']; 
if (n < 0) { 
    throw new IllegalArgumentException(); 
} 
// n contains the digit value 

आपको बेंचमार्क अन्य तरीकों के खिलाफ इस विधि (जैसे Jon Skeet's प्रत्यक्ष विधि) निर्धारित करने के लिए जो आपके आवेदन के लिए सबसे तेजी से हो जाएगा चाहिए।

+3

पहले श्रेणी की जांच को हटाया जाना चाहिए: यह केवल रूपांतरण को धीमा कर देता है; सरणी इसकी सीमाओं की जांच करेगा; अपवाद अलग होगा, लेकिन हम गति के लिए सबकुछ व्यापार कर रहे हैं, है ना? –

+0

यह एक अच्छा विचार है, और यदि आप अपवाद प्रकार के बारे में चिंतित हैं तो आप सरणी अपवाद पकड़ सकते हैं और एक अलग फेंक सकते हैं। –

13

एक हैश तालिका अपेक्षाकृत धीमी होगी। यह बहुत तेज़ है:

if (c >= '0' && c <= '9') 
{ 
    return c - '0'; 
} 
if (c >= 'a' && c <= 'f') 
{ 
    return c - 'a' + 10; 
} 
if (c >= 'A' && c <= 'F') 
{ 
    return c - 'A' + 10; 
} 
throw new IllegalArgumentException(); 

एक और विकल्प स्विच/केस कथन का प्रयास करना होगा। यदि यह कैश में है तो एक सरणी ठीक हो सकती है, लेकिन एक मिस महंगा हो सकती है।

char c = 'a'; 
System.out.println(c + "->" + Character.getNumericValue(c)); 

प्रिंटों 'a-> 10' की तरह आप उदाहरण के लिए करना चाहते हैं:

+1

मैं जो कहता हूं वह तीन बार सच है। –

+0

आईक, कोई विचार नहीं कि वहां क्या हुआ। मोबाइल से पोस्ट करने के लिए मुझे यही मिलता है। संपादित करेंगे –

+0

इसमें कुछ बदलाव हैं, और आम तौर पर यह सबसे तेज़ होगा, 256-बाइट लुक-अप टेबल से कम ला प्रो –

2

Character.getNumericValue (चार) एक और तरीका है। किसी और को एक हैश मैप कॉल बनाम एक स्थिर मेटोड कॉल की दक्षता पर टिप्पणी करना होगा, या आप इसे अपने लिए देख सकते हैं। हालांकि यह मेरे लिए क्लीनर/अधिक पठनीय लगता है।

2

आसान है, लेकिन धीमी गति से:

int i = Integer.parseInt(String.ValueOf(c), 16); 

तेजी:

int i = Character.digit(c, 16); 

मैं अभ्यस्त "प्रदर्शन के मुद्दों" के लिए कोई विशेष कोड का उपयोग करें। यदि आप वास्तव में अक्सर इसका उपयोग करते हैं, तो जेआईटी संकलित कोड बनाएगा और निष्पादन तेजी से हो जाएगा। अपने कोड को साफ रखें। आप ऊपर दिए गए कोड से निष्पादन समय की तुलना में एक प्रदर्शन परीक्षण और किसी भी विशेष कार्यान्वयन की कोशिश कर सकते हैं - मुझे यकीन है कि आपको महत्वपूर्ण सुधार नहीं मिलेगा।

3

एक सरणी का उपयोग करना सबसे तेज़ होना चाहिए।

एक सरणी आकार 16, 16^2, 16^3, 16^4 आदि का हो सकता है ..

बड़े समूहों में संख्या को परिवर्तित करने से प्रदर्शन में वृद्धि होगी।

एक मीठा स्थान होगा जहां यह सबसे सार्थक, संभवतः 4 अंक (64k तालिका) होगा।

+0

आप क्यों कहते हैं कि यह "सबसे तेज़" होना चाहिए? यह सब स्मृति उपयोग बनाम गणना के संतुलन पर निर्भर करता है। वह आंशिक रूप से कैशिंग पर निर्भर करेगा, आपकी विशेष मशीन आदि की मेमोरी आर्किटेक्चर आदि पर विचार करने के लिए बहुत कुछ है :) –

+1

हां - लेकिन गणना में निर्देशों के लिए स्मृति तक पहुंच शामिल है। एक चरित्र के मामले में यह बंद हो जाएगा, बेंचमार्क के रूप में, लेकिन इस मामले में भी एक सरणी लुकअप केवल एक पढ़ा जाता है। सामान्य स्थिति में, सरणी लुकअप में 4 के ब्लॉक करना बहुत तेज़ होना चाहिए। हालांकि आपके अंक ध्यान दिए गए! – pro

+0

एक द्विआधारी खोज पैटर्न का उपयोग करके, इसमें 64k लंबाई 128kb तालिका के साथ 16 पुनरावृत्तियों का समय लगेगा, जिससे अधिकांश आधुनिक हार्डवेयर पर एडम डेविस के समाधान से यह कम कुशल हो जाता है। –

2

मुझे नहीं लगता कि आप सीधे सर लुकअप को हरा सकते हैं।

static final int[] precalc = new int['f'+1]; 
static { 
    for (char c='0'; c<='9'; c++) precalc[c] = c-'0'; 
    for (char c='A'; c<='F'; c++) precalc[c] = c-'A'; 
    for (char c='a'; c<='f'; c++) precalc[c] = c-'a'; 
} 

System.out.println(precalc['f']); 
2

यहां ग्रेग के कोड का मेरा tweaked संस्करण है। मेरे बॉक्स पर यह मामूली तेज है - लेकिन शायद शोर के भीतर। यह निचले बाध्य चेक से बचाता है, और किसी भी घटाव करने की आवश्यकता नहीं है। एक 64 के सरणी बनाना और से बचने के लिए बाध्य जांच चीजों को धीमा करने के लिए दिखाई दे रही है - लेकिन फिर, इस तरह के समय के साथ यह सुनिश्चित करना लगभग असंभव है कि असली क्या है और शोर क्या है।

public class HexParser 
{ 
    private static final byte VALUES = new int['f']; 

    // Easier to get right for bozos like me (Jon) than 
    // a hard-coded array :) 
    static 
    { 
     for (int i=0; i < VALUES.length; i++) 
     { 
      VALUES[i] = (byte) -1; 
     } 
     for (int i='0'; i <= '9'; i++) 
     { 
      VALUES[i] = (byte) i-'0'; 
     } 
     for (int i='A'; i <= 'F'; i++) 
     { 
      VALUES[i] = (byte) (i-'A'+10); 
     } 
     for (int i='a'; i <= 'f'; i++) 
     { 
      VALUES[i] = (byte) (i-'a'+10); 
     } 
    } 

    public static byte parseHexChar(char c) 
    { 
     if (c > 'f') 
     { 
      throw new IllegalArgumentException(); 
     } 
     byte ret = VALUES[c]; 
     if (ret == -1) 
     { 
      throw new IllegalArgumentException(); 
     } 
     return ret; 
    } 
} 
4
int CharValues[256] = 
{ 
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,1,2,3,4,5,6,7,8,9,16,16,16,16,16,16,16, 
16,10,11,12,13,14,15,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 
16,10,11,12,13,14,15,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16 
} 

int n = CharValues[c]; 

if (n == 16) 
throw new IllegalArgumentException(); 

// n contains the digit value 
+0

मेरा मानना ​​है कि आपकी सरणी गलत है - 0 030 पर '0' शुरू होना चाहिए, आपने इसे 0x2D से शुरू किया है। 'ए' 0x41 पर शुरू होना चाहिए, आपने इसे 0x40 से शुरू किया है, और आप लोअर केस वर्णों का समर्थन नहीं करते हैं ('ए' 0x61 से शुरू होता है) –

+0

सहमत - मुझे लगता है कि मुझे अभी मिल गया है ... लेकिन इसकी सराहना होगी आप जांच कर रहे हैं क्योंकि यह मुझे देखकर चक्कर आ रहा है – pro

4

मैंने पहले इस विधि को देखकर याद नहीं है, लेकिन मिको रैंटनेन प्रश्न पर एक टिप्पणी में इस समीकरण ने कहा, Code golf - hex to (raw) binary conversion

(char | 32) % 39 - 9 

मैं नहीं जानता कि यह क्या होगा बेंचमार्क के रूप में (शायद कोई इसे ऊपर बेंचमार्क में जोड़ सकता है और इसे चला सकता है, लेकिन मुझे लगता है कि% प्रदर्शन प्रदर्शन को मारता है) - लेकिन यह सिंगल कैरेक्टर हेक्साडेसिमल के दशमलव रूपांतरण के लिए एक साफ, सरल एक-लाइनर है। हैंडल 0-9, ए-एफ, ए-एफ।

+0

मॉड्यूल प्रदर्शन को मारता है। हालांकि, यह सबसे तेज़ समाधान होगा यदि आप जावा को निष्पादन योग्य में संकलित कर रहे हैं क्योंकि कई आधुनिक कंप्यूटरों पर, रैम एक्सेस की गति थोड़ी धीमी है, तो लुकअप टेबल वास्तव में प्रदर्शन को कम कर देता है यदि आप हेक्साडेसिमल प्राप्त करने के लिए राम तक पहुंच रहे हैं आप के साथ काम कर रहे हैं –

2

यह ध्यान देने योग्य है कि आप अपने अधिकांश परीक्षणों में% ऑपरेशन का समय निकाल रहे हैं। इस ऑपरेशन में कुछ अन्य विकल्पों के समान समय लगता है।

private static byte lookUpTest(int i) { 
    return (byte) cs[i%cs.length]; 
} 
+0

पूरी तरह से सहमत हुए। % एक बहुत धीमी ऑपरेशन है। – njzk2

2

दोस्त, मैं माइक्रोकंट्रोलर प्रोग्रामर हूं और इस छोटी दुनिया में, गति वास्तव में मायने रखती है। सबसे तेज़ तरीका (0x0A के लिए 'ए' से) बाइट के लिए एक 'ASCII' अंकों कन्वर्ट करने के लिए कोड के इस छोटे से टुकड़े उपयोग कर रहा है

c|=0x20; 
return c<='9'? c+0xD0 : c+0xA9; 

अगर आप ascii तालिका देखने के लिए इस आदेश का जादू, सरल है आप ' 0x3_ से शुरू होने वाली संख्याएं और क्रमशः कॉलम 0x4_ और 0x6_ पर अक्षरों को मिलेगा। 1) यदि आप 0x20 के साथ आने वाले चार (परिवर्तनीय "सी") हैं, तो आप कभी भी संख्याएं नहीं बदलेंगे ... लेकिन अक्षरों के लिए आप अपरकेस को लोअरकेस में परिवर्तित कर देंगे। चूंकि हम केवल ए..एफ (ए..एफ) मूल्यों की तलाश में हैं ... मुझे दूसरों की परवाह नहीं है। 2) अब जादू। घटाव से बचने के लिए, मैं योजक ऑपरेटरों का उपयोग करता हूं। 0xD0 -0x30 का वही है, और 0xA9 -aa + 10 के समान है;

तुलना द्वारा उत्पन्न शाखा निर्देश बहुत सरल है और तालिका लुकअप का ओवरहेड नहीं लेता है और न ही "मोड" या अन्य ऑपरेटरों को नहीं लेता है!

का आनंद लें ...

+0

यह जावा के 16-बिट वर्णों के लिए काम नहीं करता है, लेकिन मुझे लगता है कि इसे अनुकूलित किया जा सकता है। –

1

16-बिट मूल्यों की एक तालिका जहां एक समय में दो अंक ऊपर दिखाई दे सकता है 8 बिट-मूल्य अन्य उत्तर में सुझाव दिया सरणियों की तुलना में तेज किया जाना चाहिए। आप जितनी जल्दी डेटा को दोहराते हैं, अक्सर आधे आधे तक पहुंचते हैं, और बाइट्स के बजाए शॉर्ट्स तक पहुंचते हैं, जिनमें से सभी सीपीयू को कम करने के लिए कम करते हैं और इसे करने के लिए और अधिक करते हैं।

8-बिट मान 0 <= i < 256 के लिए x[Integer.toHexString(i).charAt[0]] = i के रूप में प्रीकंप्यूट किए जाएंगे। फिर आप हेक्स स्ट्रिंग में प्रत्येक char c के लिए x[c] देखेंगे। आप शायद प्रत्येक हेक्स मान के ऊपरी-मामले और निचले-केस प्रकार दोनों के लिए प्रविष्टियों को डुप्लिकेट करना चाहते हैं।

32-बिट मानों की एक तालिका भी तेज होनी चाहिए, इस पर निर्भर करता है कि आप कितनी जगह ले सकते हैं।

संबंधित मुद्दे

 संबंधित मुद्दे