2016-01-04 8 views
25

टीएल; डीआर: सिस्टम को लपेटना क्यों है। न्यूमेरिक्स। वेक्टर महंगे हैं, और क्या मैं इसके बारे में कुछ भी कर सकता हूं?सिस्टम को लपेटने के लिए महंगा। Numerics.VectorX - क्यों?

00007FFDA3F94500 lea   rax,[rcx+rdx] 
00007FFDA3F94504 ret 

और 86:

[MethodImpl(MethodImplOptions.NoInlining)] 
private static long GetIt(long a, long b) 
{ 
    var x = AddThem(a, b); 
    return x; 
} 

private static long AddThem(long a, long b) 
{ 
    return a + b; 
} 

यह (64) में जीत जाएगा:

कोड का निम्न भाग पर विचार करें

00EB2E20 push  ebp 
00EB2E21 mov   ebp,esp 
00EB2E23 mov   eax,dword ptr [ebp+10h] 
00EB2E26 mov   edx,dword ptr [ebp+14h] 
00EB2E29 add   eax,dword ptr [ebp+8] 
00EB2E2C adc   edx,dword ptr [ebp+0Ch] 
00EB2E2F pop   ebp 
00EB2E30 ret   10h 

अब, अगर मैं इस लपेट एक संरचना, उदाहरण के लिए

public struct SomeWrapper 
{ 
    public long X; 
    public SomeWrapper(long X) { this.X = X; } 
    public static SomeWrapper operator +(SomeWrapper a, SomeWrapper b) 
    { 
     return new SomeWrapper(a.X + b.X); 
    } 
} 

और GetIt बदलें, उदा।

private static long GetIt(long a, long b) 
{ 
    var x = AddThem(new SomeWrapper(a), new SomeWrapper(b)).X; 
    return x; 
} 
private static SomeWrapper AddThem(SomeWrapper a, SomeWrapper b) 
{ 
    return a + b; 
} 

JITted परिणाम अब भी है बिल्कुल यदि स्थानीय प्रकार सीधे (AddThem, और SomeWrapper अतिभारित ऑपरेटर और निर्माता सभी inlined कर रहे हैं) का उपयोग कर के रूप में ही। जैसा सोचा था।

अब, अगर मैं इसे सिम-सक्षम प्रकारों के साथ आज़माता हूं, उदा। System.Numerics.Vector4:

[MethodImpl(MethodImplOptions.NoInlining)] 
private static Vector4 GetIt(Vector4 a, Vector4 b) 
{ 
    var x = AddThem(a, b); 
    return x; 
} 

इसे में JITted है:

00007FFDA3F94640 vmovupd  xmm0,xmmword ptr [rdx] 
00007FFDA3F94645 vmovupd  xmm1,xmmword ptr [r8] 
00007FFDA3F9464A vaddps  xmm0,xmm0,xmm1 
00007FFDA3F9464F vmovupd  xmmword ptr [rcx],xmm0 
00007FFDA3F94654 ret 

हालांकि, अगर मैं Vector4 एक struct में (पहला उदाहरण के समान) लपेट:

public struct SomeWrapper 
{ 
    public Vector4 X; 

    [MethodImpl(MethodImplOptions.AggressiveInlining)] 
    public SomeWrapper(Vector4 X) { this.X = X; } 

    [MethodImpl(MethodImplOptions.AggressiveInlining)] 
    public static SomeWrapper operator+(SomeWrapper a, SomeWrapper b) 
    { 
     return new SomeWrapper(a.X + b.X); 
    } 
} 
[MethodImpl(MethodImplOptions.NoInlining)] 
private static Vector4 GetIt(Vector4 a, Vector4 b) 
{ 
    var x = AddThem(new SomeWrapper(a), new SomeWrapper(b)).X; 
    return x; 
} 

मेरी कोड है एक बहुत अधिक में जोड़ा गया:

00007FFDA3F84A02 sub   rsp,0B8h 
00007FFDA3F84A09 mov   rsi,rcx 
00007FFDA3F84A0C lea   rdi,[rsp+10h] 
00007FFDA3F84A11 mov   ecx,1Ch 
00007FFDA3F84A16 xor   eax,eax 
00007FFDA3F84A18 rep stos dword ptr [rdi] 
00007FFDA3F84A1A mov   rcx,rsi 
00007FFDA3F84A1D vmovupd  xmm0,xmmword ptr [rdx] 
00007FFDA3F84A22 vmovupd  xmmword ptr [rsp+60h],xmm0 
00007FFDA3F84A29 vmovupd  xmm0,xmmword ptr [rsp+60h] 
00007FFDA3F84A30 lea   rax,[rsp+90h] 
00007FFDA3F84A38 vmovupd  xmmword ptr [rax],xmm0 
00007FFDA3F84A3D vmovupd  xmm0,xmmword ptr [r8] 
00007FFDA3F84A42 vmovupd  xmmword ptr [rsp+50h],xmm0 
00007FFDA3F84A49 vmovupd  xmm0,xmmword ptr [rsp+50h] 
00007FFDA3F84A50 lea   rax,[rsp+80h] 
00007FFDA3F84A58 vmovupd  xmmword ptr [rax],xmm0 
00007FFDA3F84A5D vmovdqu  xmm0,xmmword ptr [rsp+90h] 
00007FFDA3F84A67 vmovdqu  xmmword ptr [rsp+40h],xmm0 
00007FFDA3F84A6E vmovdqu  xmm0,xmmword ptr [rsp+80h] 
00007FFDA3F84A78 vmovdqu  xmmword ptr [rsp+30h],xmm0 
00007FFDA3F84A7F vmovdqu  xmm0,xmmword ptr [rsp+40h] 
00007FFDA3F84A86 vmovdqu  xmmword ptr [rsp+20h],xmm0 
00007FFDA3F84A8D vmovdqu  xmm0,xmmword ptr [rsp+30h] 
00007FFDA3F84A94 vmovdqu  xmmword ptr [rsp+10h],xmm0 
00007FFDA3F84A9B vmovups  xmm0,xmmword ptr [rsp+20h] 
00007FFDA3F84AA2 vmovups  xmm1,xmmword ptr [rsp+10h] 
00007FFDA3F84AA9 vaddps  xmm0,xmm0,xmm1 
00007FFDA3F84AAE lea   rax,[rsp] 
00007FFDA3F84AB2 vmovupd  xmmword ptr [rax],xmm0 
00007FFDA3F84AB7 vmovdqu  xmm0,xmmword ptr [rsp] 
00007FFDA3F84ABD vmovdqu  xmmword ptr [rsp+70h],xmm0 
00007FFDA3F84AC4 vmovups  xmm0,xmmword ptr [rsp+70h] 
00007FFDA3F84ACB vmovupd  xmmword ptr [rsp+0A0h],xmm0 
00007FFDA3F84AD5 vmovupd  xmm0,xmmword ptr [rsp+0A0h] 
00007FFDA3F84ADF vmovupd  xmmword ptr [rcx],xmm0 
00007FFDA3F84AE4 add   rsp,0B8h 
00007FFDA3F84AEB pop   rsi 
00007FFDA3F84AEC pop   rdi 
00007FFDA3F84AED ret 

ऐसा लगता है कि जेआईटी ने अब किसी कारण से फैसला किया है कि यह केवल रजिस्टरों का उपयोग नहीं कर सकता है, और इसके बजाय अस्थायी चर का उपयोग करता है, लेकिन मुझे समझ में नहीं आ रहा है कि क्यों। सबसे पहले मैंने सोचा कि यह एक संरेखण मुद्दा हो सकता है, लेकिन तब मुझे समझ में नहीं आ रहा है कि यह पहली बार xmm0 में क्यों लोड हो रहा है और फिर स्मृति के लिए यात्रा करने का निर्णय ले रहा है।

यहां क्या हो रहा है? और सबसे महत्वपूर्ण बात यह है कि, क्या मैं इसे ठीक कर सकता हूं?

कारण यह है कि मैं इस तरह की संरचना को लपेटना चाहता हूं कि मेरे पास लॉटसी कोड का है जो एपीआई का उपयोग करता है जिसका कार्यान्वयन कुछ सिमड भलाई से लाभान्वित होगा।

संपादित: तो, कुछ coreclr source में चारों ओर खुदाई के बाद, मुझे पता चला है कि यह वास्तव System.Numerics कक्षाओं के बारे में कुछ खास नहीं है। मुझे बस अपनी विधियों में System.Numerics.JitIntrinsic विशेषता जोड़नी है। इसके बाद जेआईटी मेरे कार्यान्वयन को अपने आप से बदल देगा। JitIntrinsic निजी है? कोई समस्या नहीं, बस इसे कॉपी + पेस्ट करें। मूल प्रश्न अभी भी बनी हुई है (भले ही अब मेरे पास कोई कामकाज हो)।

उत्तर

0

समस्या इस तथ्य से आती है कि एक वेक्टर 4 में 4 लम्बे होते हैं और डायरेक्टएक्स वेक्टर 4 में 4 फ्लोट होते हैं।प्रत्येक मामले में केवल एक्स को जोड़ने के लिए वैक्टर पास करना कोड को और अधिक जटिल बनाता है क्योंकि डब्ल्यू, वाई और जेड को अपरिवर्तित होने पर भी कॉपी किया जाना चाहिए। वेक्टरों को प्रत्येक "नए SomeWrapper (v)" के दौरान और चर के परिणाम को प्रभावित करने के लिए आखिरी बार फ़ंक्शन के बाहर प्रतिलिपि बनाई जाती है।

संरचना कोड अनुकूलित करना बहुत मुश्किल है। संरचना के साथ आप ढेर आवंटन समय बचाते हैं, लेकिन कई प्रतियों के कारण कोड अधिक लंबा हो जाता है।

दो बातें आप मदद कर सकते हैं:

1) रैपर का प्रयोग न करें, लेकिन विस्तार तरीकों से बचने के आवरण में कॉपी।

2) मूल्यों को वापस करने के लिए नए वैक्टर आवंटित न करें, लेकिन जब संभव हो तो उनमें से एक का उपयोग करें (कोड को अनुकूलित करें, लेकिन अन्य अंकगणितीय प्रकारों की तरह इनवेरिएंट बनाने में मदद न करें, इसलिए चरम सावधानी के साथ उपयोग करें)।

नमूना:

struct Vector 
{ 
    public long X; 
    public long Y; 
} 

static class VectorExtension 
{ 
    public static void AddToMe(this Vector v, long x, long y) 
    { 
     v.X += x; 
     v.Y += y; 
    } 

    public static void AddToMe(this Vector v, Vector v2) 
    { 
     v.X += v2.X; 
     v.Y += v2.Y; 
    } 
} 
+0

सभी फ़ील्ड तैरते हैं। सिमड केस को छोड़कर स्ट्रक्चर रैपिंग को रेखांकित किया गया है। आपके उदाहरण में कोड आम तौर पर आवश्यक नहीं है। मेरा सवाल यह है कि यह सिम मामले में क्यों टूट जाता है? (जैसा कि मैंने अपने अपडेट में लिखा था, मैं एक स्वीकार्य कामकाज खोजने में सक्षम था) – Krumelur

1

खराब प्रदर्शन लपेटकर Numerics.Vector एक संकलक मुद्दा था और ठीक 2017 20 जनवरी को गुरु के लिए प्रतिबद्ध था:

https://github.com/dotnet/coreclr/issues/7508

मैं नहीं जानता इस प्रोजेक्ट पर प्रचार कैसे काम करता है, लेकिन ऐसा लगता है कि फिक्स 2.0.0 release का हिस्सा होगा।

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