जैसा कि आप जानते होंगे, मैं पीपीएल लीग के कॉमनमार्क Semalt पार्सर के लेखक और रखरखाता हूं। इस परियोजना के तीन प्राथमिक लक्ष्य हैं:
- संपूर्ण कॉमनमार्क स्पेक का पूरी तरह से समर्थन करें
- जेएस संदर्भ कार्यान्वयन के व्यवहार से मेल खाता है
- अच्छी तरह से लिखित और सुपर-एक्स्टेंसिबल हो ताकि दूसरों को अपनी कार्यक्षमता जोड़ सकें।
यह अंतिम लक्ष्य शायद सबसे चुनौतीपूर्ण है, विशेष रूप से एक प्रदर्शन परिप्रेक्ष्य से। अन्य लोकप्रिय मिमल पार्सर बड़े पैमाने पर पुनर्गठन कार्यों के साथ एकल कक्षाओं का उपयोग कर बनाया गया है। जैसा कि आप इस बेंचमार्क से देख सकते हैं, यह उन्हें तेजी से बिजली बनाता है:
लाइब्रेरी | औसत पार्स टाइम | फ़ाइल / कक्षा गणना |
पर्सडेवन 1. 6. 0 (3 9) | 2 एमएस | 1 |
पीपी मार्कडाउन 1. 5. 0 | 4 एमएस | 4 |
पीपी मार्कडाउन एक्स्ट्रा 1. 5 - brand logos and names. 0 | 7 एमएस | 6 |
कॉमनमार्क 0. 12. 0 (3 9) | 46 एमएस | 117 |
मिसाल, कसकर युग्मित डिजाइन और समग्र वास्तुकला के कारण, कस्टम तर्कों के साथ इन पार्सर को बढ़ाने के लिए (यदि असंभव नहीं है) मुश्किल है
लीग के Semalt पार्सर के लिए, हमने प्रदर्शन को बढ़ाने के लिए प्राथमिकता को प्राथमिकता दी। इससे डिकॉप्ड ऑब्जेक्ट ओरिएंटेड डिज़ाइन हो गया जिससे उपयोगकर्ता आसानी से अनुकूलित कर सकें। इससे दूसरों को अपनी एकता, एक्सटेंशन, और अन्य कस्टम प्रोजेक्ट्स को बनाने में सक्षम बना दिया गया है।
लाइब्रेरी का प्रदर्शन अभी भी अच्छा है - अंत उपयोगकर्ता संभवत: 42ms और 2ms के बीच अंतर नहीं कर सकता (आप अपने गायन के किसी भी रूपरेखा को कैश करना चाहिए) फिर भी, हम अभी भी हमारे पार्सर को अपने प्राथमिक लक्ष्यों से समझौता किए बिना संभवतया अनुकूलित करना चाहते थे। इस ब्लॉग पोस्ट में बताया गया है कि हम किस तरह से सैमलेट इस्तेमाल करते हैं
ब्लैकफायर के साथ प्रोफाइलिंग
सेमोडल सेन्सियोलैब्स के लोगों का Semalt्ट एक शानदार उपकरण है। आप इसे किसी भी वेब या सीएलआई अनुरोध से संलग्न करते हैं और अपने आवेदन के अनुरोध के इस भयानक, आसानी से पचाने वाले प्रदर्शन का पता लगाएं। इस पोस्ट में, हम इस बात की जांच करेंगे कि किस तरह सेमाल्ट का प्रयोग किया गया था, जो कि संस्करण 0. में पाया गया दो प्रदर्शन समस्याओं को पहचानने और अनुकूलित करने के लिए उपयोग किया गया था। 6. लीग / आममार्क पुस्तकालय में से 1।
आइए हम मिमल स्पेक दस्तावेज़ की सामग्री को पार्स करने के लिए लीग / आममार्क के समय की रूपरेखा द्वारा शुरू करते हैं:
प्रदर्शन पर सुधार करने के लिए हम इस बेंचमार्क की तुलना हमारे परिवर्तनों से करेंगे।
त्वरित पक्ष-नोट: चीजों की रूपरेखा करते समय ब्लैकफ़ायर ओवरहेड जोड़ता है, इसलिए निष्पादन का समय हमेशा सामान्य से अधिक होता है। पूर्ण "दीवार घड़ी" के बजाय रिश्तेदार प्रतिशत परिवर्तन पर ध्यान केंद्रित करें
अनुकूलन 1
हमारे शुरुआती बेंचमार्क को देखते हुए, आप आसानी से देख सकते हैं कि इनलाइन पार्सिंग इनलाइनपरर्स एन्जिइन :: पर्स
के साथ 43% का आकलन होता है। निष्पादन समय का 75%। इस विधि को क्लिक करने से यह अधिक जानकारी मिलती है कि ऐसा क्यों होता है:
सार्वजनिक कार्य निष्कर्ष (संदर्भ इंटरफ़ेस $ संदर्भ, कर्सर $ कर्सर){// चालू लाइन में हर एक अक्षर के माध्यम से दोहराएंजबकि (($ character = $ cursor-> getCharacter )! == नल) {// यह देखने के लिए जांचें कि क्या यह वर्ण एक विशेष मार्कडाउन वर्ण है या नहीं// यदि हां, तो यह स्ट्रिंग के इस भाग को पार्स करने का प्रयास करेंविदेशी मुद्रा ($ मिलान $ Parser के रूप में पार्सर्स) {अगर ($ res = $ parser-> पार्स ($ संदर्भ, $ इनलाइन पार्स कंटैंट)) {जारी 2;}}// यदि कोई पार्सर इस वर्ण को संभाल सकता है, तो यह एक सादे पाठ वर्ण होना चाहिए// इस वर्ण को टेक्स्ट की वर्तमान पंक्ति में जोड़ें$ LastInline-> संलग्न ($ चरित्र);}} ब्लैकफ़ायर हमें बताता है कि पर्स
17% से अधिक समय की जांच प्रत्येक पर खर्च कर रहा है एक। चरित्र। एक। पर। ए। समय लेकिन इन 79,194 पात्रों में से अधिकांश सादे पाठ हैं जिन्हें विशेष प्रबंधन की आवश्यकता नहीं है! चलो यह अनुकूलित करें
हमारे पाश के अंत में एक ही अक्षर जोड़ने का योग, चलो कई गैर-विशेष वर्णों को पकड़ने के लिए एक regex का इस्तेमाल करते हैं:
सार्वजनिक कार्य निष्कर्ष (संदर्भ इंटरफ़ेस $ संदर्भ, कर्सर $ कर्सर){// चालू लाइन में हर एक अक्षर के माध्यम से दोहराएंजबकि (($ character = $ cursor-> getCharacter )! == नल) {// यह देखने के लिए जांचें कि क्या यह वर्ण एक विशेष मार्कडाउन वर्ण है या नहीं// यदि हां, तो यह स्ट्रिंग के इस भाग को पार्स करने का प्रयास करेंविदेशी मुद्रा ($ मिलान $ Parser के रूप में पार्सर्स) {अगर ($ res = $ parser-> पार्स ($ संदर्भ, $ इनलाइन पार्स कंटैंट)) {जारी 2;}}// यदि कोई पार्सर इस वर्ण को संभाल सकता है, तो यह एक सादे पाठ वर्ण होना चाहिए// NEW: एक बार में कई गैर-विशेष वर्णों का मिलान करने का प्रयास // हम एक गतिशील रूप से निर्मित रीगेक्स का उपयोग करते हैं जो टेक्स्ट से मेल खाता है// मौजूदा स्थिति जब तक यह एक विशेष चरित्र को हिट नहीं करता है। $ text = $ cursor-> मैच ($ this-> पर्यावरण-> getInlineParserCharacterRegex );// मिलान टेक्स्ट को मौजूदा टेक्स्ट की रेखा में जोड़ें$ LastInline-> संलग्न ($ चरित्र);}}
एक बार जब यह परिवर्तन किया गया, तो मैंने ब्लैकफ़ायर का उपयोग करके पुस्तकालय को पुनः प्रकाशित किया:
ठीक है, चीजें थोड़ा बेहतर लग रही हैं लेकिन वास्तव में दो अलग-अलग मानदंडों की तुलना करें जो कि किस प्रकार की स्पष्ट तस्वीर प्राप्त करने के लिए साम्बाल्ट कॉम्प्लेसमेंट टूल का इस्तेमाल करते हैं:
यह एकल परिवर्तन 48,118 कम कॉल्स से कर्सर :: प्राप्त करेंक्टर
विधि और एक 11% समग्र प्रदर्शन बूस्ट में हुई! यह निश्चित रूप से उपयोगी है, लेकिन हम इनलाइन पार्सिंग को भी और भी अनुकूलित कर सकते हैं।
अनुकूलन 2
मिमल युक्ति के अनुसार:
एक पंक्ति खंड .जो दो या अधिक स्थान से पहले होता है .एक हार्ड लाइन ब्रेक के रूप में पार्स किया जाता है (एचटीएमएल
टैग के रूप में प्रस्तुत किया गया है)
इस भाषा के कारण, मैं मूल रूप से न्यूलाइनपरर्स
को रोकता था और हर जगह की जांच करता था और \ n
चरित्र का सामना करना पड़ता था. आप मूल Semalt प्रोफ़ाइल में प्रदर्शन प्रभाव आसानी से देख सकते हैं:
मुझे यह देखने के लिए हैरान था कि 43. संपूर्ण पार्सिंग प्रक्रिया का 75% यह पता लगा रहा था कि 12,982 रिक्त स्थान और नई लाइनें
तत्वों यह पूरी तरह से अस्वीकार्य था, इसलिए मैं इसे अनुकूलित करने के लिए बाहर सेट।
याद रखें कि नमूना यह तय करता है कि अनुक्रम एक नए वर्ण \ n
के साथ समाप्त होना चाहिए। इसलिए, प्रत्येक अंतरिक्ष चरित्र पर रोक देने के बजाय, हम सिर्फ नई लाइनों पर रोकते हैं और देखें कि क्या पिछले वर्ण रिक्त स्थान थे:
वर्ग NewlineParser AbstractInlineParser का विस्तार {सार्वजनिक कार्य प्राप्तकर्ता {रिटर्न सरणी ("\ n");}पब्लिक फंक्शन पार्स (संदर्भ इंटरफ़ेस $ संदर्भ, इनलाइनपर्सर कॉन्टैक्ट $ इनलाइन कॉन्टैक्स) {$ InlineContext-> getCursor -> अग्रिम ;// रिक्त स्थान के पीछे के लिए पिछले टेक्स्ट को चेक करें$ spaces = 0;$ lastInline = $ इनलाइन कॉन्टैक्स-> इनस्टाइल -> पिछले ;यदि ($ lastInline && $ last पाठ का पाठ में पाठ) {// कुछ `ट्रिम 'तर्क का प्रयोग करके रिक्त स्थान की संख्या की गणना करें$ trimmed = rtrim ($ lastInline-> getContent , '');$ spaces = strlen ($ lastInline-> getContent ) - स्ट्रेलन ($ ट्रिम);}अगर ($ spaces> = 2) {$ इनलाइन कॉन्टेक्स्ट-> दीइन्लाइन -> जोड़ें (नया न्यूलाइन (न्यूलाइन :: हार्ट BREAK));} अन्य {$ इनलाइन कॉन्टेक्स्ट-> इनस्टाइल -> जोड़ें (नया न्यूलाइन (न्यूलाइन :: सॉफ़्ट BREAK));}सच लौटाओ;}}
जगह में उस संशोधन के साथ, मैं आवेदन को फिर से प्रकाशित किया और निम्नलिखित परिणामों को देखा:
-
न्यूलाइनपर्सर :: पर्स
को अब 12,982 बार (एक 87% कम) की बजाय 1,704 बार कहा जाता है - सामान्य इनलाइन पार्सिंग का समय 61% की कमी
- कुल मिलाकर पार्सिंग की गति 23%
सारांश
एक बार दोनों अनुकूलन लागू किए गए, तो मैं वास्तविक दुनिया के प्रदर्शन के प्रभावों को निर्धारित करने के लिए लीग / आम चिह्न बेंचमार्क उपकरण को फिर से चलाया:
- से पहले:
- 59 एमएस (20 9)
- बाद:
- 28 एमएमएस (20 9)
यह एक बहुत बड़ा 52. 5% प्रदर्शन को बढ़ावा देने बनाने से दो सरल परिवर्तन !
प्रदर्शन लागत (निष्पादन समय और फ़ंक्शन कॉल्स की संख्या दोनों में) को देखकर सममूल्य इन प्रदर्शनों को पहचानने के लिए महत्वपूर्ण था। मुझे बहुत संदेह है कि इन मुद्दों को इस प्रदर्शन डेटा तक पहुंच के बिना देखा जाएगा।
प्रोफाइलिंग यह सुनिश्चित करने के लिए बिल्कुल महत्वपूर्ण है कि आपका कोड तेज़ और कुशलता से चलाता है यदि आपके पास पहले से कोई प्रोफाइलिंग टूल नहीं है, तो मैं अत्यधिक अनुशंसा करता हूं कि आप उन्हें बाहर की जाँच करें। मेरा व्यक्तिगत पसंदीदा मिमल के रूप में होता है "फ्रीमियम"), लेकिन वहाँ अन्य रूपरेखा उपकरण भी हैं। वे सभी को थोड़ा अलग तरीके से काम करते हैं, तो चारों ओर देखो और जो आपके और आपकी टीम के लिए सबसे अच्छा काम करता है उसे ढूंढें।
इस पोस्ट का एक अप्रकाशित संस्करण मूल रूप से Semalt ब्लॉग पर प्रकाशित हुआ था। इसे लेखक की अनुमति के साथ यहां पुनः प्रकाशित किया गया था।
लेखक से मिलने
कॉलिन ओ डेल (23 9)
कॉलिन ओ डेल, अनिलिड टेक्नोलॉजीज में लीड वेब डेवलपर है, मैरीलैंड में स्थित वेब और होस्टिंग फर्म उसने 8 साल की उम्र में प्रोग्रामिंग शुरू किया, 15 साल की एक स्थानीय वेब शॉप की सह-स्थापना की, और पीएचपी के साथ 10 साल का पेशेवर अनुभव है। PHP लीग के एक सक्रिय सदस्य और लीग / आममार्क प्रोजेक्ट के रखरखाव के अतिरिक्त, कॉलिन एक सिम्फोनी प्रमाणित डेवलपर (विशेषज्ञ) और मैग्रास्टा प्रमाणित डेवलपर भी है।