ప్రతిబింబాన్ని వేగవంతం చేయడం గురించి విజయవంతం కాని కథనం

నేను వెంటనే వ్యాసం యొక్క శీర్షికను వివరిస్తాను. సరళమైన కానీ వాస్తవిక ఉదాహరణను ఉపయోగించి ప్రతిబింబం యొక్క ఉపయోగాన్ని ఎలా వేగవంతం చేయాలనే దానిపై మంచి, నమ్మదగిన సలహా ఇవ్వడం అసలు ప్రణాళిక, కానీ బెంచ్‌మార్కింగ్ సమయంలో ప్రతిబింబం నేను అనుకున్నంత నెమ్మదిగా లేదని తేలింది, LINQ నా పీడకలల కంటే నెమ్మదిగా ఉంది. అయితే చివరికి నేను కొలతల్లో కూడా తప్పు చేశానని తేలింది... ఈ జీవిత కథ వివరాలు కట్ కింద మరియు వ్యాఖ్యలలో ఉన్నాయి. ఉదాహరణ చాలా సాధారణమైనది మరియు సాధారణంగా ఒక సంస్థలో చేసే సూత్రప్రాయంగా అమలు చేయబడినందున, ఇది చాలా ఆసక్తికరంగా మారింది, నాకు అనిపించినట్లుగా, జీవితం యొక్క ప్రదర్శన: వ్యాసం యొక్క ప్రధాన విషయం యొక్క వేగంపై ప్రభావం బాహ్య తర్కం కారణంగా గుర్తించబడలేదు: Moq, Autofac, EF కోర్ మరియు ఇతర "బ్యాండింగ్‌లు".

నేను ఈ వ్యాసం యొక్క ప్రభావంతో పని చేయడం ప్రారంభించాను: ప్రతిబింబం ఎందుకు నెమ్మదిగా ఉంటుంది

మీరు చూడగలిగినట్లుగా, అప్లికేషన్‌ను బాగా వేగవంతం చేయడానికి రిఫ్లెక్షన్ టైప్ మెథడ్స్‌ని నేరుగా కాల్ చేయడానికి బదులుగా కంపైల్డ్ డెలిగేట్‌లను ఉపయోగించమని రచయిత సూచిస్తున్నారు. వాస్తవానికి, IL ఉద్గారం ఉంది, కానీ నేను దానిని నివారించాలనుకుంటున్నాను, ఎందుకంటే ఇది పనిని నిర్వహించడానికి అత్యంత శ్రమతో కూడిన మార్గం, ఇది లోపాలతో నిండి ఉంది.

ప్రతిబింబం యొక్క వేగం గురించి నేను ఎల్లప్పుడూ ఇదే విధమైన అభిప్రాయాన్ని కలిగి ఉన్నానని పరిగణనలోకి తీసుకుంటే, నేను రచయిత యొక్క తీర్మానాలను ప్రశ్నించడానికి ప్రత్యేకంగా ఉద్దేశించలేదు.

ఎంటర్‌ప్రైజ్‌లో ప్రతిబింబం యొక్క అమాయక వినియోగాన్ని నేను తరచుగా ఎదుర్కొంటాను. రకం తీసుకోబడింది. ఆస్తికి సంబంధించిన సమాచారం తీసుకుంటారు. SetValue పద్ధతి అంటారు మరియు అందరూ ఆనందిస్తారు. టార్గెట్ ఫీల్డ్‌లో విలువ వచ్చింది, అందరూ సంతోషంగా ఉన్నారు. చాలా తెలివైన వ్యక్తులు - సీనియర్లు మరియు టీమ్ లీడ్‌లు - ఒక రకానికి చెందిన "యూనివర్సల్" మ్యాపర్‌ల అమాయకమైన అమలు ఆధారంగా, ఆబ్జెక్ట్‌కు వారి పొడిగింపులను వ్రాస్తారు. సారాంశం సాధారణంగా ఇలా ఉంటుంది: మేము అన్ని ఫీల్డ్‌లను తీసుకుంటాము, అన్ని లక్షణాలను తీసుకుంటాము, వాటిపై మళ్ళించాము: రకం సభ్యుల పేర్లు సరిపోలితే, మేము SetValueని అమలు చేస్తాము. కాలానుగుణంగా మేము ఒక రకంలో కొంత ఆస్తిని కనుగొనని తప్పుల కారణంగా మినహాయింపులను పొందుతాము, కానీ ఇక్కడ కూడా పనితీరును మెరుగుపరిచే మార్గం ఉంది. ప్రయత్నించండి/పట్టుకోండి.

వ్యక్తులు తమ ముందు వచ్చిన యంత్రాలు ఎలా పని చేస్తాయనే సమాచారంతో పూర్తిగా ఆయుధాలు లేకుండా పార్సర్‌లు మరియు మ్యాపర్‌లను తిరిగి ఆవిష్కరించడం నేను చూశాను. ప్రజలు తమ అమాయక అమలులను వ్యూహాల వెనుక, ఇంటర్‌ఫేస్‌ల వెనుక, ఇంజెక్షన్‌ల వెనుక దాచడం నేను చూశాను, ఇది తదుపరి బచ్చనాలియాను క్షమించేలా చేస్తుంది. అలాంటి సాక్షాత్కారాల వద్ద నేను నా ముక్కును తిప్పాను. వాస్తవానికి, నేను నిజమైన పనితీరు లీక్‌ను కొలవలేదు మరియు వీలైతే, నేను నా చేతుల్లోకి వస్తే అమలును మరింత “ఆప్టిమల్”గా మార్చాను. అందువల్ల, క్రింద చర్చించిన మొదటి కొలతలు నన్ను తీవ్రంగా గందరగోళానికి గురి చేశాయి.

మీలో చాలామంది, రిక్టర్ లేదా ఇతర భావజాలవేత్తలను చదివేటప్పుడు, కోడ్‌లో ప్రతిబింబం అనేది అప్లికేషన్ యొక్క పనితీరుపై చాలా ప్రతికూల ప్రభావాన్ని చూపే ఒక దృగ్విషయం అని పూర్తిగా న్యాయమైన ప్రకటనను చూశారని నేను భావిస్తున్నాను.

కాలింగ్ రిఫ్లెక్షన్ CLRకి అవసరమైన వాటిని కనుగొనడానికి, వారి మెటాడేటాను పైకి లాగడానికి, వాటిని అన్వయించడానికి, మొదలైన వాటి కోసం అసెంబ్లీల ద్వారా వెళ్ళేలా చేస్తుంది. అదనంగా, సీక్వెన్స్‌లను దాటుతున్నప్పుడు ప్రతిబింబం పెద్ద మొత్తంలో మెమరీని కేటాయించడానికి దారితీస్తుంది. మేము మెమరీని ఉపయోగిస్తున్నాము, CLR GCని వెలికితీస్తుంది మరియు ఫ్రైజ్‌లు ప్రారంభమవుతాయి. ఇది గమనించదగ్గ నెమ్మదిగా ఉండాలి, నన్ను నమ్మండి. ఆధునిక ఉత్పత్తి సర్వర్‌లు లేదా క్లౌడ్ మెషీన్‌లలో భారీ మొత్తంలో మెమరీ అధిక ప్రాసెసింగ్ జాప్యాలను నిరోధించదు. వాస్తవానికి, ఎక్కువ మెమరీ, GC ఎలా పనిచేస్తుందో మీరు గమనించే అవకాశం ఉంది. ప్రతిబింబం, సిద్ధాంతంలో, అతనికి అదనపు ఎరుపు గుడ్డ.

అయినప్పటికీ, మనమందరం IoC కంటైనర్‌లు మరియు తేదీ మ్యాపర్‌లను ఉపయోగిస్తాము, దీని నిర్వహణ సూత్రం కూడా ప్రతిబింబంపై ఆధారపడి ఉంటుంది, అయితే వాటి పనితీరు గురించి సాధారణంగా ఎటువంటి ప్రశ్నలు ఉండవు. లేదు, ఎందుకంటే బాహ్య పరిమిత సందర్భ నమూనాల నుండి డిపెండెన్సీల పరిచయం మరియు సంగ్రహణ చాలా అవసరం కాబట్టి మనం ఏ సందర్భంలోనైనా పనితీరును త్యాగం చేయాల్సి ఉంటుంది. ప్రతిదీ సరళమైనది - ఇది నిజంగా పనితీరును ప్రభావితం చేయదు.

వాస్తవం ఏమిటంటే, ప్రతిబింబ సాంకేతికతపై ఆధారపడిన అత్యంత సాధారణ ఫ్రేమ్‌వర్క్‌లు దానితో మరింత ఉత్తమంగా పనిచేయడానికి అన్ని రకాల ఉపాయాలను ఉపయోగిస్తాయి. సాధారణంగా ఇది కాష్. సాధారణంగా ఇవి వ్యక్తీకరణలు మరియు వ్యక్తీకరణ ట్రీ నుండి సంకలనం చేయబడిన ప్రతినిధులు. అదే ఆటోమ్యాపర్, రిఫ్లెక్షన్‌ని పిలవకుండానే ఒకదానికొకటి మార్చగలిగే ఫంక్షన్‌లతో రకాలను సరిపోలే పోటీ నిఘంటువుని నిర్వహిస్తుంది.

ఇది ఎలా సాధించబడింది? ముఖ్యంగా, JIT కోడ్‌ను రూపొందించడానికి ప్లాట్‌ఫారమ్ ఉపయోగించే లాజిక్‌కి ఇది భిన్నంగా లేదు. ఒక పద్ధతిని మొదటిసారిగా పిలిచినప్పుడు, అది సంకలనం చేయబడుతుంది (మరియు, అవును, ఈ ప్రక్రియ వేగంగా లేదు); తదుపరి కాల్‌లలో, నియంత్రణ ఇప్పటికే కంపైల్ చేయబడిన పద్ధతికి బదిలీ చేయబడుతుంది మరియు గణనీయమైన పనితీరు తగ్గింపులు ఉండవు.

మా విషయంలో, మీరు JIT సంకలనాన్ని కూడా ఉపయోగించవచ్చు మరియు దాని AOT ప్రతిరూపాల వలె అదే పనితీరుతో సంకలనం చేయబడిన ప్రవర్తనను ఉపయోగించవచ్చు. ఈ సందర్భంలో వ్యక్తీకరణలు మా సహాయానికి వస్తాయి.

ప్రశ్నలోని సూత్రాన్ని క్లుప్తంగా ఈ క్రింది విధంగా రూపొందించవచ్చు:
మీరు కంపైల్ చేసిన ఫంక్షన్‌ని కలిగి ఉన్న ప్రతినిధిగా ప్రతిబింబం యొక్క తుది ఫలితాన్ని కాష్ చేయాలి. వస్తువుల వెలుపల నిల్వ చేయబడిన మీ రకం, వర్కర్ ఫీల్డ్‌లలో టైప్ సమాచారంతో అవసరమైన అన్ని వస్తువులను కాష్ చేయడం కూడా అర్ధమే.

ఇందులో లాజిక్ ఉంది. ఇంగితజ్ఞానం ఏదైనా కంపైల్ చేసి కాష్ చేయగలిగితే, అది చేయాలి అని చెబుతుంది.

ముందుకు చూస్తే, మీరు వ్యక్తీకరణలను కంపైల్ చేసే ప్రతిపాదిత పద్ధతిని ఉపయోగించకపోయినా, ప్రతిబింబంతో పని చేయడంలో కాష్ దాని ప్రయోజనాలను కలిగి ఉందని చెప్పాలి. వాస్తవానికి, నేను పైన ప్రస్తావించిన వ్యాస రచయిత యొక్క థీసిస్‌లను ఇక్కడ నేను పునరావృతం చేస్తున్నాను.

ఇప్పుడు కోడ్ గురించి. తీవ్రమైన క్రెడిట్ సంస్థ యొక్క తీవ్రమైన ఉత్పత్తిలో నేను ఎదుర్కొన్న నా ఇటీవలి నొప్పి ఆధారంగా ఒక ఉదాహరణను చూద్దాం. అన్ని అంశాలు కల్పితం కాబట్టి ఎవరూ ఊహించలేరు.

కొంత సారాంశం ఉంది. కాంటాక్ట్ ఉండనివ్వండి. ప్రామాణిక శరీరంతో అక్షరాలు ఉన్నాయి, వాటి నుండి పార్సర్ మరియు హైడ్రేటర్ ఇదే పరిచయాలను సృష్టిస్తాయి. ఒక లేఖ వచ్చింది, మేము దానిని చదివాము, కీ-విలువ జంటలుగా అన్వయించాము, పరిచయాన్ని సృష్టించాము మరియు దానిని డేటాబేస్లో సేవ్ చేసాము.

ఇది ప్రాథమికమైనది. ఒక పరిచయానికి పూర్తి పేరు, వయస్సు మరియు సంప్రదింపు ఫోన్ లక్షణాలు ఉన్నాయని అనుకుందాం. ఈ డేటా లేఖలో ప్రసారం చేయబడింది. అక్షరం యొక్క బాడీలో ఎంటిటీ ప్రాపర్టీలను జతగా మ్యాపింగ్ చేయడానికి కొత్త కీలను త్వరగా జోడించగలిగేలా వ్యాపారానికి మద్దతు కావాలి. ఎవరైనా టెంప్లేట్‌లో అక్షరదోషం చేసినట్లయితే లేదా విడుదలకు ముందు కొత్త పార్ట్‌నర్ నుండి అత్యవసరంగా మ్యాపింగ్‌ని ప్రారంభించాల్సిన అవసరం ఉన్నట్లయితే, కొత్త ఆకృతికి అనుగుణంగా ఉంటుంది. అప్పుడు మనం చౌక డేటాఫిక్స్‌గా కొత్త మ్యాపింగ్ సహసంబంధాన్ని జోడించవచ్చు. అంటే జీవిత ఉదాహరణ.

మేము అమలు చేస్తాము, పరీక్షలను సృష్టించండి. పనిచేస్తుంది.

నేను కోడ్‌ను అందించను: చాలా మూలాలు ఉన్నాయి మరియు అవి వ్యాసం చివర లింక్ ద్వారా GitHubలో అందుబాటులో ఉన్నాయి. మీరు వాటిని లోడ్ చేయవచ్చు, గుర్తించలేని విధంగా హింసించవచ్చు మరియు వాటిని కొలవవచ్చు, ఇది మీ విషయంలో ప్రభావితం చేస్తుంది. నేను స్లోగా ఉండాల్సిన హైడ్రేటర్ నుండి వేగంగా ఉండాల్సిన హైడ్రేటర్‌ను వేరు చేసే రెండు టెంప్లేట్ పద్ధతుల కోడ్‌ను మాత్రమే ఇస్తాను.

తర్కం క్రింది విధంగా ఉంది: టెంప్లేట్ పద్ధతి ప్రాథమిక పార్సర్ లాజిక్ ద్వారా రూపొందించబడిన జతలను పొందుతుంది. LINQ లేయర్ అనేది పార్సర్ మరియు హైడ్రేటర్ యొక్క ప్రాథమిక తర్కం, ఇది డేటాబేస్ సందర్భానికి అభ్యర్థన చేస్తుంది మరియు పార్సర్ నుండి జతలతో కీలను పోలుస్తుంది (ఈ ఫంక్షన్‌ల కోసం పోలిక కోసం LINQ లేకుండా కోడ్ ఉంది). తరువాత, జతలు ప్రధాన ఆర్ద్రీకరణ పద్ధతికి పంపబడతాయి మరియు జతల విలువలు ఎంటిటీ యొక్క సంబంధిత లక్షణాలకు సెట్ చేయబడతాయి.

“ఫాస్ట్” (బెంచ్‌మార్క్‌లలో ఫాస్ట్ ఉపసర్గ):

 protected override Contact GetContact(PropertyToValueCorrelation[] correlations)
        {
            var contact = new Contact();
            foreach (var setterMapItem in _proprtySettersMap)
            {
                var correlation = correlations.FirstOrDefault(x => x.PropertyName == setterMapItem.Key);
                setterMapItem.Value(contact, correlation?.Value);
            }
            return contact;
        }

మనం చూడగలిగినట్లుగా, సెట్టర్ లక్షణాలతో కూడిన స్టాటిక్ సేకరణ ఉపయోగించబడుతుంది - సెట్టర్ ఎంటిటీని పిలిచే కంపైల్డ్ లాంబ్డాస్. కింది కోడ్ ద్వారా సృష్టించబడింది:

        static FastContactHydrator()
        {
            var type = typeof(Contact);
            foreach (var property in type.GetProperties())
            {
                _proprtySettersMap[property.Name] = GetSetterAction(property);
            }
        }

        private static Action<Contact, string> GetSetterAction(PropertyInfo property)
        {
            var setterInfo = property.GetSetMethod();
            var paramValueOriginal = Expression.Parameter(property.PropertyType, "value");
            var paramEntity = Expression.Parameter(typeof(Contact), "entity");
            var setterExp = Expression.Call(paramEntity, setterInfo, paramValueOriginal).Reduce();
            
            var lambda = (Expression<Action<Contact, string>>)Expression.Lambda(setterExp, paramEntity, paramValueOriginal);

            return lambda.Compile();
        }

సాధారణంగా ఇది స్పష్టంగా ఉంటుంది. మేము ప్రాపర్టీలను పర్యవేక్షిస్తాము, సెటర్‌లను పిలిచే వారి కోసం డెలిగేట్‌లను సృష్టించండి మరియు వాటిని సేవ్ చేస్తాము. అప్పుడు మేము అవసరమైనప్పుడు కాల్ చేస్తాము.

“స్లో” (బెంచ్‌మార్క్‌లలో స్లో ప్రిఫిక్స్):

        protected override Contact GetContact(PropertyToValueCorrelation[] correlations)
        {
            var contact = new Contact();
            foreach (var property in _properties)
            {
                var correlation = correlations.FirstOrDefault(x => x.PropertyName == property.Name);
                if (correlation?.Value == null)
                    continue;

                property.SetValue(contact, correlation.Value);
            }
            return contact;
        }

ఇక్కడ మేము వెంటనే ప్రాపర్టీలను దాటవేసి, నేరుగా SetValueకి కాల్ చేస్తాము.

స్పష్టత కోసం మరియు సూచనగా, నేను వాటి సహసంబంధ జతల విలువలను నేరుగా ఎంటిటీ ఫీల్డ్‌లలోకి వ్రాసే అమాయక పద్ధతిని అమలు చేసాను. ఉపసర్గ - మాన్యువల్.

ఇప్పుడు BenchmarkDotNet తీసుకొని పనితీరును పరిశీలిద్దాం. మరియు హఠాత్తుగా... (స్పాయిలర్ - ఇది సరైన ఫలితం కాదు, వివరాలు క్రింద ఉన్నాయి)

ప్రతిబింబాన్ని వేగవంతం చేయడం గురించి విజయవంతం కాని కథనం

మనం ఇక్కడ ఏమి చూస్తాము? స్లో ప్రిఫిక్స్‌తో ఉన్న పద్ధతుల కంటే ఫాస్ట్ ప్రిఫిక్స్‌ను విజయవంతంగా కలిగి ఉండే పద్ధతులు దాదాపు అన్ని పాస్‌లలో నెమ్మదిగా ఉంటాయి. ఇది కేటాయింపు మరియు పని వేగం రెండింటికీ వర్తిస్తుంది. మరోవైపు, సాధ్యమైన చోట LINQ పద్ధతులను ఉపయోగించి మ్యాపింగ్ యొక్క అందమైన మరియు సొగసైన అమలు, దీనికి విరుద్ధంగా, ఉత్పాదకతను బాగా తగ్గిస్తుంది. వ్యత్యాసం క్రమంలో ఉంది. వేర్వేరు సంఖ్యల పాస్‌లతో ట్రెండ్ మారదు. స్కేల్‌లో మాత్రమే తేడా. LINQతో ఇది 4 - 200 రెట్లు నెమ్మదిగా ఉంటుంది, ఇంచుమించు అదే స్థాయిలో చెత్త ఎక్కువగా ఉంటుంది.

నవీకరించబడింది

నేను నా కళ్ళను నమ్మలేదు, కానీ మరీ ముఖ్యంగా, మా సహోద్యోగి నా కళ్ళను లేదా నా కోడ్‌ను నమ్మలేదు - డిమిత్రి టిఖోనోవ్ 0x1000000. నా పరిష్కారాన్ని రెండుసార్లు తనిఖీ చేసిన తర్వాత, అతను అద్భుతంగా కనుగొన్నాడు మరియు అమలులో అనేక మార్పుల కారణంగా నేను తప్పిపోయిన లోపాన్ని ఎత్తి చూపాడు. Moq సెటప్‌లో కనుగొనబడిన బగ్‌ను పరిష్కరించిన తర్వాత, అన్ని ఫలితాలు అమల్లోకి వచ్చాయి. పునఃపరీక్ష ఫలితాల ప్రకారం, ప్రధాన ధోరణి మారదు - LINQ ఇప్పటికీ ప్రతిబింబం కంటే పనితీరును ఎక్కువగా ప్రభావితం చేస్తుంది. అయినప్పటికీ, వ్యక్తీకరణ సంకలనంతో పని ఫలించలేదు మరియు ఫలితం కేటాయింపు మరియు అమలు సమయం రెండింటిలోనూ కనిపిస్తుంది. మొదటి ప్రయోగ, స్టాటిక్ ఫీల్డ్‌లు ప్రారంభించబడినప్పుడు, సహజంగా "ఫాస్ట్" పద్ధతికి నెమ్మదిగా ఉంటుంది, కానీ అప్పుడు పరిస్థితి మారుతుంది.

పునఃపరీక్ష ఫలితం ఇక్కడ ఉంది:

ప్రతిబింబాన్ని వేగవంతం చేయడం గురించి విజయవంతం కాని కథనం

ముగింపు: ఒక సంస్థలో ప్రతిబింబాన్ని ఉపయోగిస్తున్నప్పుడు, ప్రత్యేకంగా ఉపాయాలను ఆశ్రయించాల్సిన అవసరం లేదు - LINQ ఉత్పాదకతను ఎక్కువగా తింటుంది. అయినప్పటికీ, ఆప్టిమైజేషన్ అవసరమయ్యే అధిక-లోడ్ పద్ధతులలో, మీరు ఇనిషియలైజర్లు మరియు డెలిగేట్ కంపైలర్ల రూపంలో ప్రతిబింబాన్ని సేవ్ చేయవచ్చు, ఇది "ఫాస్ట్" లాజిక్‌ను అందిస్తుంది. ఈ విధంగా మీరు ప్రతిబింబం యొక్క వశ్యత మరియు అప్లికేషన్ యొక్క వేగం రెండింటినీ నిర్వహించవచ్చు.

బెంచ్‌మార్క్ కోడ్ ఇక్కడ అందుబాటులో ఉంది. ఎవరైనా నా పదాలను ఒకటికి రెండుసార్లు తనిఖీ చేయవచ్చు:
హబ్రా రిఫ్లెక్షన్ పరీక్షలు

PS: పరీక్షలలోని కోడ్ IoCని ఉపయోగిస్తుంది మరియు బెంచ్‌మార్క్‌లలో ఇది స్పష్టమైన నిర్మాణాన్ని ఉపయోగిస్తుంది. వాస్తవం ఏమిటంటే, తుది అమలులో నేను పనితీరును ప్రభావితం చేసే మరియు ఫలితాన్ని ధ్వనించే అన్ని అంశాలను కత్తిరించాను.

PPS: వినియోగదారుకు ధన్యవాదాలు డిమిత్రి టిఖోనోవ్ @0x1000000 Moqని సెటప్ చేయడంలో నా లోపాన్ని కనుగొన్నందుకు, ఇది మొదటి కొలతలను ప్రభావితం చేసింది. పాఠకులలో ఎవరికైనా తగినంత కర్మ ఉంటే, దయచేసి లైక్ చేయండి. మనిషి ఆగిపోయాడు, మనిషి చదివాడు, మనిషి రెండుసార్లు తనిఖీ చేసి తప్పును ఎత్తి చూపాడు. ఇది గౌరవం మరియు సానుభూతికి అర్హమైనదని నేను భావిస్తున్నాను.

PPPS: స్టైల్ మరియు డిజైన్‌లో దిగువ స్థాయికి చేరుకున్న ఖచ్చితమైన రీడర్‌కు ధన్యవాదాలు. నేను ఏకరూపత మరియు సౌలభ్యం కోసం ఉన్నాను. ప్రదర్శన యొక్క దౌత్యం కోరుకునేది చాలా మిగిలి ఉంది, కానీ నేను విమర్శలను పరిగణనలోకి తీసుకున్నాను. నేను ప్రక్షేపకం కోసం అడుగుతున్నాను.

మూలం: www.habr.com

ఒక వ్యాఖ్యను జోడించండి