Инстанцирането на променлива на локален низ влияе ли върху производителността?

Имам две ситуации:

    static void CreateCopyOfString()
    {
        string s = "Hello";
        ProcessString(s);
    }

и

    static void DoNotCreateCopyOfString()
    {
        ProcessString("Hello");
    }

IL за тези две ситуации изглежда така:

    .method private hidebysig static void  CreateCopyOfString() cil managed
    {
        // Code size       15 (0xf)
        .maxstack  1
        .locals init ([0] string s)
        IL_0000:  nop
        IL_0001:  ldstr      "Hello"
        IL_0006:  stloc.0
        IL_0007:  ldloc.0
        IL_0008:  call       void ConsoleApplication1.Program::ProcessString(string)
        IL_000d:  nop
        IL_000e:  ret
    } // end of method Program::CreateCopyOfString

и

    .method private hidebysig static void  DoNotCreateCopyOfString() cil managed
    {
          // Code size       13 (0xd)
          .maxstack  8
          IL_0000:  nop
          IL_0001:  ldstr      "Hello"
          IL_0006:  call       void ConsoleApplication1.Program::ProcessString(string)
          IL_000b:  nop
          IL_000c:  ret
    } // end of method Program::DoNotCreateCopyOfString

В първия случай има допълнителни повиквания за string init, stloc.0 и ldloc.0. Това означава ли, че първият случай ще се представи по-слабо от втория случай, когато низът се предава директно на метода, вместо първо да се съхранява в локалната променлива?

Видях въпроса Влияе ли инициализацията на локална променлива с нула върху производителността?, но изглежда е малко по-различно от това, което трябва да знам тук. Благодаря.


person ashtee    schedule 22.03.2013    source източник
comment
Как компилирахте кода? Беше ли в режим Debug или Release? Вярвам, че в Release и двата IL ще изглеждат абсолютно еднакви.   -  person MarcinJuraszek    schedule 23.03.2013
comment
Беше компилиран в debug. Нека проверя с режим на освобождаване.   -  person ashtee    schedule 23.03.2013
comment
Същият резултат с версията на версията.   -  person ashtee    schedule 23.03.2013


Отговори (2)


Гледате неоптимизирания IL, от една страна - оттук и всичките "nop". Може да откриете, че генерира различен код при изграждането на Release версията.

Дори и с неоптимизираната версия, ако работите под оптимизиращ JIT, бих очаквал да завърши със същия JITted код.

Дори с неоптимизиращ JIT, който направи всъщност генерира код, който върши повече работа при всяко извикване на това, бих се изненадал да видя, че това има значително въздействие във всяко реално приложение.

Както винаги:

  • Поставете цели за ефективност, преди да започнете, и измерете спрямо тях.
  • Разберете кои решения ще бъдат трудни за коригиране по-късно по отношение на ефективността и се тревожете за тях много повече от решения като това, които могат да бъдат променени по-късно без въздействие другаде.
  • Напишете най-простия и четлив код, който ще работи първи.
  • Ако това не работи достатъчно добре, проучете дали извършването на промени, които вредят на четливостта, помага на производителността достатъчно, за да оправдае болката.
person Jon Skeet    schedule 22.03.2013
comment
Не, освен ако вашият vm не изпълни il, като го изпрати по имейл до отдалечен сървър за изпълнение... Чудя се каква е относителната цена в цикли на двойка stloc/ldloc? Вероятно ще бъде оптимизиран в регистър, мислите ли? - person JerKimball; 23.03.2013
comment
@JerKimball: Доста вероятно. Но един оптимизиращ JIT наистина не трябва да бъде ужасно умен, за да забележи това :) - person Jon Skeet; 23.03.2013
comment
Освен наличието на nops, не виждам никаква разлика в компилациите за отстраняване на грешки и версията. - person ashtee; 23.03.2013
comment
Сигурно е, че четливостта е по-добра с предишния подход с по-сложно формиране на низ, използвайки нещо като string.Format. Това е мястото, където обсъждах дали локална променлива да държи низа и след това да го предава на следния метод или не. - person ashtee; 23.03.2013
comment
@ashtee: Няма нужда от дебат - просто напишете простия код. Сравнете сложността на анализирането на форматиращ низ, изграждането на този нов низ и т.н. с възможната работа (която вероятно дори не е налице след JITting) за съхраняване на препратка и след това зареждането й отново. - person Jon Skeet; 23.03.2013

Не, няма да повлияе на производителността. Можете да потвърдите това, като се уверите, че машинният код, произведен и за двете, е един и същ. Имайте предвид, че в оптимизиран JIT, ProcessString може да бъде вграден. За да избегнете това, можете да добавите [MethodImpl(MethodImplOptions.NoInlining)]. Компилирайте оптимизирана (Release) компилация.

  1. Отворете изпълнимия файл в WinDbg. Използвайте съответстваща 32 или 64-битова версия в зависимост от вашия EXE.
  2. Въведете sxe ld clrjit, за да прекъснете, когато clrjit.dll се зареди. Въведете g, за да продължите до прекъсване.
  3. Заредете SOS с .loadby sos clr. Обърнете внимание, че за по-ранни версии на CLR трябва да използвате mscorwks вместо clr.
  4. Намерете адреса на таблицата с методи с !name2ee * <full class name>.
  5. Въведете !dumpmt -md <address of MethoTable>, за да изхвърлите подробности за метода. Забележете в момента, че CreateCopyOfString и DoNotCreateCopyOfString все още не са JITed.
  6. Въведете !bpmd <full class name>.CreateCopyOfString и !bpmd <full class name>.DoNotCreateCopyOfString, за да прекъснете, когато се извика метод. Въведете g, за да продължите. Може също да използва !bpmd -md <address of MethodDesc> за задаване на точки на прекъсване.
  7. Когато точката на прекъсване е достигната, въведете !u <address of MethodDesc>, за да изхвърлите машинния код за метода.

Обърнете внимание, че когато опитах това, само един от методите беше JITed, вероятно защото времето за изпълнение определи, че двата метода са идентични и JITing другия беше ненужен. Като такъв, коментирах обаждането като подходящо и повторих, за да получа машинния код.

Действителните регистри и адреси ще варират, но и двата метода водят до следния машинен код:

sub     rsp,28h
mov     rcx,121E3258h
mov     rcx,qword ptr [rcx]
call    000007fe`9852c038
nop
add     rsp,28h
ret

Следователно можете да заключите, че тъй като се изпълнява един и същ машинен код, производителността на двата метода ще бъде еднаква.

person Dono    schedule 23.03.2013
comment
Благодаря за пояснението. - person ashtee; 23.03.2013