it-swarm.asia

Postfix Artış İşlecinden Kaçının

Ben performans nedenleri nedeniyle postfix artış operatörü kaçının okumuştum (bazı durumlarda).

Ancak bu kod okunabilirliğini etkilemez mi? Bence:

for(int i = 0; i < 42; i++);
    /* i will never equal 42! */

Şundan daha iyi görünüyor:

for(int i = 0; i < 42; ++i);
    /* i will never equal 42! */

Ama bu muhtemelen alışkanlıktan uzak. Kuşkusuz, çok fazla kullanım görmedim ++i.

Bu durumda okunabilirliği feda etmek kötü bir performans mı? Yoksa sadece kör müyüm ve ++i, i++ 'Dan daha okunabilir mi?

25
Mateen Ulhaq

Gerçekler:

  1. i ++ ve ++ i eşit derecede kolay okunur. Birini sevmiyorsunuz, çünkü buna alışkın değilsiniz, ama aslında onu yanlış yorumlayabileceğiniz hiçbir şey yok, bu yüzden okumak veya yazmak için artık çalışma yok.

  2. En azından bazı durumlarda, postfix operatörü daha az verimli olacaktır.

  3. Bununla birlikte,% 99,99 vakada önemli değil, çünkü (a) yine de basit veya ilkel bir tür üzerinde hareket edecek ve sadece büyük bir nesneyi kopyalamak bir sorun (b) bir performansta olmayacak kodun (c) kritik kısmı, derleyicinin onu optimize edip etmeyeceğini bilmiyor olabilir.

  4. Bu nedenle, özellikle postfix'e girmek için iyi bir alışkanlık olmadıkça öneki kullanmanızı öneririm, çünkü (a) diğer şeylerle hassas olmak iyi bir alışkanlıktır ve (b) mavi ayda bir kez postfix'i kullanmak isteyeceksiniz ve yanlış bir şekilde anlatın: her zaman ne demek istediğinizi yazarsanız, bu daha az olasıdır. Performans ve optimizasyon arasında her zaman bir denge vardır.

İhtiyacınız olana kadar sağduyunuzu kullanmalı ve mikro-optimize etmemelisiniz, ancak ikisi için de şaşırtıcı derecede verimsiz olmamalısınız. Tipik olarak bu şu anlama gelir: ilk olarak, zaman açısından kritik olmayan kodlarda bile kabul edilemez derecede verimsiz olan herhangi bir kod yapısını dışlayın (normalde 500MB nesnelerini değersiz olarak değere geçirmek gibi temel bir kavramsal hatayı temsil eden bir şey); ve ikincisi, kodu yazmanın diğer tüm yollarından en netini seçin.

Ancak, burada cevabın basit olduğuna inanıyorum: Özellikle postfix'e ihtiyacınız olmadığı sürece önek yazmanın (a) çok az net ve (b) çok daha verimli olma olasılığının daha yüksek olduğuna inanıyorum, bu yüzden bunu her zaman varsayılan olarak yazmalısınız, ancak unutursanız endişelenmeyin.

Altı ay önce, seninle aynı şeyi düşündüm, i ++ 'ın daha doğal olduğunu, ancak tamamen alışık olduğun şey olduğunu düşündüm.

DÜZENLEME 1: Scott Meyers, "Bu konuda genel olarak güvenilen" Daha Etkili C++ ", genel olarak kullanıcı tanımlı türlerde postfix operatörünü kullanmaktan kaçınmanız gerektiğini söylüyor (çünkü postfix artış fonksiyonunun tek aklı uygulaması bir nesneyi kopyalayın, artışı gerçekleştirmek için önek artış işlevini çağırın ve kopyayı geri gönderin, ancak kopyalama işlemleri pahalı olabilir).

Bu nedenle, (a) bugün doğru olup olmadığı hakkında genel bir kural olup olmadığını bilmiyoruz, (b) aynı zamanda içsel türler için de geçerli olup olmadığı (daha az) (c) üzerinde "++" kullanmanız gerekip gerekmediği hafif bir yineleyici sınıfından daha fazlası. Ama yukarıda tarif ettiğim tüm nedenlerden dolayı, daha önce söylediğim şeyi yapmak önemli değil.

DÜZENLEME 2: Bu genel uygulamayı ifade eder. Belirli bir durumda bunun önemli olduğunu düşünüyorsanız, profilinizi oluşturmalı ve görmelisiniz. Profil oluşturma kolay ve ucuzdur ve işe yarar. Optimize edilmesi gereken şeylerin ilk prensiplerden çıkarılması zor ve pahalıdır ve işe yaramaz.

58
Jack V.

Daima önce programcıyı, sonra da bilgisayarı kodlayın.

Bir performans farkı varsa, derleyici uzman gözünü kodunuza uyguladıktan sonra VE ölçebilirsiniz VE önemlidir - o zaman değiştirebilirsiniz.

62
Martin Beckett

GCC her iki döngü için de aynı makine kodunu üretir.

C Kodu

int main(int argc, char** argv)
{
    for (int i = 0; i < 42; i++)
            printf("i = %d\n",i);

    for (int i = 0; i < 42; ++i)
        printf("i = %d\n",i);

    return 0;
}

Montaj Kodu (yorumlarımla)

    cstring
LC0:
    .ascii "i = %d\12\0"
    .text
.globl _main
_main:
    pushl   %ebp
    movl    %esp, %ebp
    pushl   %ebx
    subl    $36, %esp
    call    L9
"L00000000001$pb":
L9:
    popl    %ebx
    movl    $0, -16(%ebp)  // -16(%ebp) is "i" for the first loop 
    jmp L2
L3:
    movl    -16(%ebp), %eax   // move i for the first loop to the eax register 
    movl    %eax, 4(%esp)     // Push i onto the stack
    leal    LC0-"L00000000001$pb"(%ebx), %eax // load the effective address of the format string into the eax register
    movl    %eax, (%esp)      // Push the address of the format string onto the stack
    call    L_printf$stub    // call printf
    leal    -16(%ebp), %eax  // make the eax register point to i
    incl    (%eax)           // increment i
L2:
    cmpl    $41, -16(%ebp)  // compare i to the number 41
    jle L3              // jump to L3 if less than or equal to 41
    movl    $0, -12(%ebp)   // -12(%ebp) is "i" for the second loop  
    jmp L5
L6:
    movl    -12(%ebp), %eax   // move i for the second loop to the eax register 
    movl    %eax, 4(%esp)     // Push i onto the stack
    leal    LC0-"L00000000001$pb"(%ebx), %eax // load the effective address of the format string into the eax register
    movl    %eax, (%esp)      // Push the address of the format string onto the stack
    call    L_printf$stub     // call printf
    leal    -12(%ebp), %eax  // make eax point to i
    incl    (%eax)           // increment i
L5:
    cmpl    $41, -12(%ebp)   // compare i to 41 
    jle L6               // jump to L6 if less than or equal to 41
    movl    $0, %eax
    addl    $36, %esp
    popl    %ebx
    leave
    ret
    .section __IMPORT,__jump_table,symbol_stubs,self_modifying_code+pure_instructions,5
L_printf$stub:
    .indirect_symbol _printf
    hlt ; hlt ; hlt ; hlt ; hlt
    .subsections_via_symbols
13
bit-twiddler

Performans konusunda endişelenmeyin, zamanın% 97'sini söyleyin. Erken Optimizasyon tüm Kötülüğün Köküdür.

- Donald Knuth

Şimdi bu yolumuzdan çıktı, seçimimizi aklı başında yapalım :

  • ++i: önek artışı , geçerli değeri arttırır ve sonucu verir
  • i++: düzeltme sonrası artışı , değeri kopyala, geçerli değeri artır, kopyayı verir

Eski değerin bir kopyası gerekmedikçe, postfix artışını kullanmak işleri halletmenin yuvarlak bir yoludur.

Yanlışlık tembellikten gelir, her zaman niyetinizi en doğrudan şekilde ifade eden yapıyı kullanın, gelecekteki koruyucunun orijinal niyetinizi yanlış anlayabileceğinden daha az şans vardır.

Burada (gerçekten) küçük olmasına rağmen, kodu okuyarak gerçekten şaşkın olduğum zamanlar var: Niyetin ve gerçek ifadenin çakışıp çakışmadığını gerçekten merak ediyordum ve elbette, birkaç ay sonra onlar (veya ben) hatırlamıyorum da ...

Bu yüzden, size doğru görünüp görünmediği önemli değildir. Kucaklama ÖPÜCÜK . Birkaç ay içinde eski uygulamalarınızdan vazgeçeceksiniz.

12
Matthieu M.

C++ ile, özellikle aşırı kod yazıyorsanız ve yineleyicilerin ne olduğunu bilmiyorsanız, operatör aşırı yükleri varsa önemli bir performans farkı yaratabilirsiniz. Herhangi bir yineleyici X'in arkasındaki mantık hem önemli hem de önemli olabilir - yani derleyici tarafından yavaş ve kullanılamaz.

Ancak bu, sadece önemsiz bir tür olacağını ve performans farkının önemsiz olduğunu ve derleyicinin kolayca optimize edebileceğini bildiğiniz C'de durum böyle değildir.

Yani bir ipucu: C veya C++ 'da program yaparsınız ve sorular ikisiyle değil, biriyle veya diğeriyle ilgilidir.

4
DeadMG

Her iki işlemin performansı da temeldeki mimariye bağlıdır. Kişi hafızada saklanan bir değeri arttırmalıdır, bu da von Neumann darboğazının her iki durumda da sınırlayıcı faktör olduğu anlamına gelir.

++ i durumunda,

Fetch i from memory 
Increment i
Store i back to memory
Use i

İ ++ durumunda,

Fetch i from memory
Use i
Increment i
Store i back to memory

++ ve - operatörleri Kökenlerini PDP-11 komut setine kadar takip eder. PDP-11, bir kayıtta otomatik artım sonrası yapabilir. Ayrıca, bir sicilde bulunan etkili bir adres üzerinde otomatik ön azaltma gerçekleştirebilir. Her iki durumda da, derleyici bu makine seviyesindeki işlemlerden ancak söz konusu değişken bir "register" değişkeni olduğunda yararlanabilir.

2
bit-twiddler

Bir şeyin yavaş olup olmadığını bilmek istiyorsanız test edin. Bir BigInteger veya eşdeğeri alın, her iki deyimi kullanarak döngüye benzer bir şekilde yapıştırın, döngünün içinin optimize edilmediğinden emin olun ve her ikisini de zamanlayın.

Makaleyi okuduktan sonra, üç nedenden dolayı çok ikna edici bulmuyorum. Birincisi, derleyici asla kullanılmayan bir nesnenin yaratılmasını optimize edebilmelidir. İki, i++ kavramı döngüler için sayısal için deyimseldir, bu yüzden gerçekten etkilendiğini gördüğüm durumlar sınırlıdır. Üç, destekleyecek sayıları olmayan tamamen teorik bir argüman sağlarlar.

Özellikle 1. nedene dayanarak, tahminim aslında zamanlamayı yaptığınızda, hemen yan yana olacaklardır.

2
jprete