Swift Beta'da bir algoritma uyguluyordum ve performansın çok kötü olduğunu fark ettim. Daha derine intikten sonra darboğazlardan birinin sıralama dizileri kadar basit bir şey olduğunu fark ettim. İlgili bölüm burada:
let n = 1000000
var x = [Int](repeating: 0, count: n)
for i in 0..<n {
x[i] = random()
}
// start clock here
let y = sort(x)
// stop clock here
C ++, benzer bir işlem bilgisayarımda 0.06s alır .
Python'da 0.6s alır (hile yok, sadece tamsayıların listesi için y = sıralanmış (x)).
Swift'te aşağıdaki komutla derlersem 6 saniye sürer :
xcrun swift -O3 -sdk `xcrun --show-sdk-path --sdk macosx`
Ve aşağıdaki komutla derlersem 88s kadar sürer :
xcrun swift -O0 -sdk `xcrun --show-sdk-path --sdk macosx`
Xcode'da "Release" ve "Debug" derlemeleri olan zamanlamalar benzerdir.
Burada yanlış olan ne? C ++ ile karşılaştırıldığında bazı performans kayıplarını anlayabildim, ancak saf Python ile karşılaştırıldığında 10 kat yavaşlama anlayamadım.
Düzenleme: hava durumu , bu kodun C ++ sürümü kadar hızlı çalışmasını -O3
sağladığını fark etti -Ofast
! Bununla birlikte, -Ofast
dilin anlambilimini çok değiştirir - testimde, tamsayı taşmaları ve dizi indeksleme taşmaları kontrollerini devre dışı bıraktı . Örneğin -Ofast
, aşağıdaki Swift kodu çökmeden sessizce çalışır (ve bazı çöpleri yazdırır):
let n = 10000000
print(n*n*n*n*n)
let x = [Int](repeating: 10, count: n)
print(x[n])
Yani -Ofast
bizim istediğimiz değil; Swift'in asıl amacı güvenlik ağlarının yerinde olmasıdır. Elbette, güvenlik ağlarının performans üzerinde bir miktar etkisi vardır, ancak programları 100 kat daha yavaş yapmamalıdır. Java'nın dizi sınırlarını zaten kontrol ettiğini ve tipik durumlarda yavaşlamanın 2'den daha az bir faktör olduğunu unutmayın. Ve Clang ve GCC'de -ftrapv
tam sayı taşmalarını (imzalı) kontrol etmek için elimizde var ve o kadar da yavaş değil.
Dolayısıyla soru: Swift'te güvenlik ağlarını kaybetmeden nasıl makul bir performans elde edebiliriz?
Edit 2: Çizgiler boyunca çok basit döngülerle biraz daha kıyaslama yaptım
for i in 0..<n {
x[i] = x[i] ^ 12345678
}
(Burada xor işlemi sadece montaj kodunda ilgili döngüyü daha kolay bulabilmem için var. Tespit edilmesi kolay ancak aynı zamanda "zararsız" bir işlem seçmeye çalıştım. tam sayı taşmalarına).
Yine, -O3
ve arasındaki performansta büyük bir fark vardı -Ofast
. Montaj koduna bir göz attım:
İle
-Ofast
hemen hemen ne beklenir olsun. İlgili bölüm, 5 makine dili talimatına sahip bir döngüdür.En
-O3
çılgın hayal gücümün ötesinde bir şey alıyorum. İç döngü 88 satır montaj kodunu kapsar. Hepsini anlamaya çalışmadım, ama en şüpheli kısım 13 çağrı "callq _swift_retain" ve 13 çağrı "callq _swift_release" dir. Yani, iç döngüde 26 altyordam çağrısı !
Düzenleme 3: Yorumlarda Ferruccio, yerleşik işlevlere (örn. Sıralama) güvenmediği anlamında adil olan karşılaştırmalar istedi. Aşağıdaki programın oldukça iyi bir örnek olduğunu düşünüyorum:
let n = 10000
var x = [Int](repeating: 1, count: n)
for i in 0..<n {
for j in 0..<n {
x[i] = x[j]
}
}
Aritmetik yoktur, bu nedenle tamsayı taşmaları hakkında endişelenmemize gerek yoktur. Yaptığımız tek şey sadece dizi referansları. Sonuçlar burada — Swift -O3, -Ofast ile karşılaştırıldığında neredeyse 500 kat azalıyor:
- C ++ -O3: 0.05 s
- C ++ -O0: 0.4 s
- Java: 0.2 s
- PyPy ile Python: 0.5 s
- Python: 12 s
- Hızlı -Ofast: 0.05 s
- Swift -O3: 23 s
- Swift -O0: 443 s
(Derleyicinin anlamsız döngüleri tamamen optimize edebileceğinden endişe ediyorsanız, bunu örneğin olarak değiştirebilir x[i] ^= x[j]
ve çıktı veren bir baskı ifadesi ekleyebilirsiniz x[0]
. Bu hiçbir şeyi değiştirmez; zamanlamalar çok benzer olacaktır.)
Ve evet, burada Python uygulaması, ints listesi olan ve döngüler için iç içe yerleştirilmiş aptal bir saf Python uygulamasıydı. Optimize edilmemiş Swift'ten çok daha yavaş olmalıdır . Swift ve dizi indeksleme ile ilgili bir şey ciddi bir şekilde kırılmış gibi görünüyor.
Edit 4: Bu sorunlar (ve diğer bazı performans sorunları) Xcode 6 beta 5'te düzeltilmiş gibi görünüyor.
Sıralama için şimdi aşağıdaki zamanlamalara sahibim:
- clang ++ -O3: 0.06 s
- swiftc -Ofast: 0.1 s
- swiftc -O: 0.1 s
- swiftc: 4 s
İç içe döngüler için:
- clang ++ -O3: 0.06 s
- swiftc -Ofast: 0,3 sn.
- swiftc -O: 0.4 s
- swiftc: 540 s
Görünüşe göre güvensiz -Ofast
(aka -Ounchecked
) kullanmak için hiçbir neden yok ; plain -O
eşit derecede iyi kod üretir.
xcrun --sdk macosx swift -O3
. Daha kısa.