web-dev-qa-db-fra.com

Swift Performances bêta: tri des tableaux

Je mettais en œuvre un algorithme dans Swift Beta et j'ai constaté que les performances étaient très médiocres. Après avoir creusé plus profondément, j'ai réalisé que l'un des goulots d'étranglement était quelque chose d'aussi simple que de trier des tableaux. La partie pertinente est ici:

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

En C++, une opération similaire prend .06s sur mon ordinateur.

En Python, il faut .6s (pas d’astuce, juste y = trié (x) pour une liste d’entiers).

En Swift cela prend 6s si je le compile avec la commande suivante:

xcrun Swift -O3 -sdk `xcrun --show-sdk-path --sdk macosx`

Et cela prend autant que 88s si je le compile avec la commande suivante:

xcrun Swift -O0 -sdk `xcrun --show-sdk-path --sdk macosx`

Les timings dans Xcode avec les versions "Release" et "Debug" sont similaires.

Qu'est-ce qui ne va pas ici? Je pouvais comprendre certaines pertes de performances par rapport à C++, mais pas un ralentissement de 10 fois par rapport à Python pur.


Edit: La météo a remarqué que le changement de -O3 en -Ofast rend ce code presque aussi rapide que la version C++! Cependant, -Ofast change beaucoup la sémantique de la langue - lors de mes tests, il désactivait les contrôles de dépassement d'entier et d'index de tablea. Par exemple, avec -Ofast, le code suivant Swift s'exécute en mode silencieux sans planter (et affiche des erreurs):

let n = 10000000
print(n*n*n*n*n)
let x =  [Int](repeating: 10, count: n)
print(x[n])

Donc, -Ofast n’est pas ce que nous voulons; le but de Swift est que nous avons mis en place les filets de sécurité. Bien entendu, les filets de sécurité ont un impact sur les performances, mais ils ne devraient pas ralentir les programmes 100 fois plus rapidement. Rappelez-vous que Java vérifie déjà les limites du tableau et que, dans des cas typiques, le ralentissement est d'un facteur beaucoup moins important que 2. Et dans Clang et GCC, nous avons -ftrapv pour la vérification du nombre entier (signé) déborde, et ce n’est pas si lent non plus.

D'où la question: comment pouvons-nous obtenir des performances raisonnables en Swift sans perdre les filets de sécurité?


Edit 2: J’ai fait un peu plus de benchmarking, avec des boucles très simples le long des lignes de

for i in 0..<n {
    x[i] = x[i] ^ 12345678
}

(Ici, l'opération xor est là pour que je puisse trouver plus facilement la boucle appropriée dans le code d'assemblage. J'ai essayé de choisir une opération facile à repérer mais également "inoffensive" en ce sens qu'elle ne devrait nécessiter aucune vérification liée débordement entier.)

Là encore, il y avait une énorme différence dans les performances entre -O3 et -Ofast. J'ai donc jeté un coup d'œil au code d'assemblage:

  • Avec -Ofast j'obtiens à peu près tout ce à quoi je m'attendais. La partie pertinente est une boucle avec 5 instructions en langage machine.

  • Avec -O3 j'obtiens quelque chose qui dépassait mon imagination la plus folle. La boucle interne couvre 88 lignes de code d'assemblage. Je n'ai pas essayé de tout comprendre, mais les éléments les plus suspects sont 13 appels de "callq _Swift_retain" et 13 autres appels de "callq _Swift_release". C’est-à-dire 26 appels de sous-routines dans la boucle interne!


Edit 3: Dans ses commentaires, Ferruccio a demandé des points de repère équitables en ce sens qu’ils ne reposaient pas sur des fonctions intégrées (par exemple, sorte). Je pense que le programme suivant est un assez bon exemple:

let n = 10000
var x = [Int](repeating: 1, count: n)
for i in 0..<n {
    for j in 0..<n {
        x[i] = x[j]
    }
}

Il n'y a pas d'arithmétique, nous n'avons donc pas à nous soucier des débordements d'entiers. La seule chose que nous faisons est juste beaucoup de références de tableau. Et les résultats sont là: Swift -O3 perd un facteur presque 500 par rapport à -Ofast:

  • C++ -O3: ,05 s
  • C++ -O0: 0.4 s
  • Java: ,2 s
  • Python avec PyPy: 0.5 s
  • Python: 12 s
  • Rapide-Rapide: 0,05 s
  • Swift -O3: 23 s
  • Rapide -O0: 443 s

(Si vous craignez que le compilateur n'optimise entièrement les boucles inutiles, vous pouvez le remplacer par exemple par x[i] ^= x[j] et ajouter une instruction d'impression générant x[0]. Cela ne change rien; le minutage être très semblable.)

Et oui, ici, l'implémentation Python était une implémentation stupide pure Python avec une liste d'ints et de boucles imbriquées. Il devrait être beaucoup plus lent que Swift non optimisé. Quelque chose semble sérieusement rompu avec Swift et l'indexation par tableau.


Edit 4: Ces problèmes (ainsi que d’autres problèmes de performances) semblent avoir été résolus dans Xcode 6 beta 5.

Pour le tri, j'ai maintenant les timings suivants:

  • clang ++ -O3: 0,06 s
  • swiftc -Ofast: 0,1 s
  • swiftc -O: 0,1 s
  • swiftc: 4 s

Pour les boucles imbriquées:

  • clang ++ -O3: 0,06 s
  • swiftc -Ofast: 0,3 s
  • swiftc -O: 0.4 s
  • swiftc: 540 s

Il semble qu’il n’y ait plus aucune raison d’utiliser le non sécurisé -Ofast (a.k.a. -Ounchecked); plain -O produit un code tout aussi bon.

908
Jukka Suomela

tl; dr Swift 1.0 est maintenant aussi rapide que C pour ce repère en utilisant le niveau d'optimisation de version par défaut [-O].


Voici un tri rapide sur place dans Swift Beta:

func quicksort_Swift(inout a:CInt[], start:Int, end:Int) {
    if (end - start < 2){
        return
    }
    var p = a[start + (end - start)/2]
    var l = start
    var r = end - 1
    while (l <= r){
        if (a[l] < p){
            l += 1
            continue
        }
        if (a[r] > p){
            r -= 1
            continue
        }
        var t = a[l]
        a[l] = a[r]
        a[r] = t
        l += 1
        r -= 1
    }
    quicksort_Swift(&a, start, r + 1)
    quicksort_Swift(&a, r + 1, end)
}

Et pareil en C:

void quicksort_c(int *a, int n) {
    if (n < 2)
        return;
    int p = a[n / 2];
    int *l = a;
    int *r = a + n - 1;
    while (l <= r) {
        if (*l < p) {
            l++;
            continue;
        }
        if (*r > p) {
            r--;
            continue;
        }
        int t = *l;
        *l++ = *r;
        *r-- = t;
    }
    quicksort_c(a, r - a + 1);
    quicksort_c(l, a + n - l);
}

Les deux fonctionnent:

var a_Swift:CInt[] = [0,5,2,8,1234,-1,2]
var a_c:CInt[] = [0,5,2,8,1234,-1,2]

quicksort_Swift(&a_Swift, 0, a_Swift.count)
quicksort_c(&a_c, CInt(a_c.count))

// [-1, 0, 2, 2, 5, 8, 1234]
// [-1, 0, 2, 2, 5, 8, 1234]

Les deux sont appelés dans le même programme que celui écrit.

var x_Swift = CInt[](count: n, repeatedValue: 0)
var x_c = CInt[](count: n, repeatedValue: 0)
for var i = 0; i < n; ++i {
    x_Swift[i] = CInt(random())
    x_c[i] = CInt(random())
}

let Swift_start:UInt64 = mach_absolute_time();
quicksort_Swift(&x_Swift, 0, x_Swift.count)
let Swift_stop:UInt64 = mach_absolute_time();

let c_start:UInt64 = mach_absolute_time();
quicksort_c(&x_c, CInt(x_c.count))
let c_stop:UInt64 = mach_absolute_time();

Ceci convertit les temps absolus en secondes:

static const uint64_t NANOS_PER_USEC = 1000ULL;
static const uint64_t NANOS_PER_MSEC = 1000ULL * NANOS_PER_USEC;
static const uint64_t NANOS_PER_SEC = 1000ULL * NANOS_PER_MSEC;

mach_timebase_info_data_t timebase_info;

uint64_t abs_to_nanos(uint64_t abs) {
    if ( timebase_info.denom == 0 ) {
        (void)mach_timebase_info(&timebase_info);
    }
    return abs * timebase_info.numer  / timebase_info.denom;
}

double abs_to_seconds(uint64_t abs) {
    return abs_to_nanos(abs) / (double)NANOS_PER_SEC;
}

Voici un résumé des niveaux d'optimisation du compilateur:

[-Onone] no optimizations, the default for debug.
[-O]     perform optimizations, the default for release.
[-Ofast] perform optimizations and disable runtime overflow checks and runtime type checks.

Temps en secondes avec [- Onone] pour n = 10_000 :

Swift:            0.895296452
C:                0.001223848

Voici la méthode sort () de Swift pour n = 10_000 :

Swift_builtin:    0.77865783

Voici [- O] pour n = 10_000 :

Swift:            0.045478346
C:                0.000784666
Swift_builtin:    0.032513488

Comme vous pouvez le constater, les performances de Swift ont été multipliées par 20.

Selon réponse des dirigeants , le réglage [- Ofast] fait toute la différence, ce qui a pour résultat ces temps pour n = 10_000 :

Swift:            0.000706745
C:                0.000742374
Swift_builtin:    0.000603576

Et pour n = 1_000_000 :

Swift:            0.107111846
C:                0.114957179
Swift_sort:       0.092688548

Pour comparaison, ceci est avec [- Onone] pour n = 1_000_000 :

Swift:            142.659763258
C:                0.162065333
Swift_sort:       114.095478272

Donc, Swift sans optimisation était presque 1000 fois plus lent que C dans cette référence, à ce stade de son développement. D'autre part, avec les deux compilateurs réglés sur [-Ofast] Swift, les performances sont au moins aussi bonnes, sinon légèrement meilleures que C.

Il a été souligné que [-Ofast] change la sémantique du langage, le rendant potentiellement dangereux. C’est ce que Apple déclare dans les notes de publication de Xcode 5.0:

Un nouveau niveau d'optimisation, Fast, disponible dans LLVM, permet des optimisations agressives. -Ofast assouplit certaines restrictions conservatrices, principalement pour les opérations en virgule flottante, qui sont sûres pour la plupart des codes. Il peut générer des gains de haute performance significatifs du compilateur.

Ils le préconisent tous. Que cela soit sage ou non, je ne saurais le dire, mais d'après ce que je peux dire, il semble assez raisonnable d'utiliser [-Ofast] dans un communiqué si vous ne faites pas de calcul arithmétique en virgule flottante de haute précision et que vous êtes certain de ne pas avoir de nombre entier. les débordements de tableau sont possibles dans votre programme. Si vous avez besoin de performances élevées et de contrôles de débordement/calcul arithmétique précis, choisissez une autre langue pour le moment.

MISE À JOUR BETA 3:

n = 10_000 avec [- O] :

Swift:            0.019697268
C:                0.000718064
Swift_sort:       0.002094721

En général, Swift est un peu plus rapide et il semble que le type intégré de Swift a considérablement changé.

MISE À JOUR FINALE:

[- Onone] :

Swift:   0.678056695
C:       0.000973914

[- O] :

Swift:   0.001158492
C:       0.001192406

[- Ounchecked] :

Swift:   0.000827764
C:       0.001078914
451
Joseph Mark

TL; DR : Oui, la seule implémentation du langage Swift est lente, pour le moment . Si vous avez besoin de code rapide, numérique (et d’autres types de code, vraisemblablement), il vous suffit d’en utiliser un autre. À l'avenir, vous devriez réévaluer votre choix. Cela peut toutefois suffire pour la plupart des applications qui sont écrites à un niveau supérieur.

D'après ce que je vois dans SIL et LLVM IR, il semble qu'ils ont besoin de plusieurs optimisations pour supprimer les retenues et les versions, qui pourraient être implémentées dans Clang (pour Objective-C), mais ne les a pas encore portés. C'est la théorie avec laquelle je vais (pour l'instant… je dois encore confirmer que Clang fait quelque chose à ce sujet), puisqu'un profileur exécuté sur le dernier test de cette question donne ce "joli" résultat:

Time profiling on -O3Time profiling on -Ofast

Comme beaucoup d’autres l’ont dit, -Ofast est totalement dangereux et change la sémantique de la langue. Pour moi, c'est à l'étape "Si vous comptez utiliser cela, utilisez simplement une autre langue". Je réévaluerai ce choix plus tard, si cela change.

-O3 nous donne un tas d'appels Swift_retain et Swift_release qui, honnêtement, ne semblent pas devoir être là pour cet exemple. L'optimiseur aurait dû élire (la plupart des) AFAICT, car il connaissait la plupart des informations sur le tableau et savait qu'il y avait (au moins) une référence forte.

Il ne devrait pas émettre plus de retenues lorsqu'il n'appelle même pas de fonctions qui pourraient libérer les objets. Je ne pense pas qu'un constructeur de tableau puisse renvoyer un tableau plus petit que ce qui avait été demandé, ce qui signifie que beaucoup de vérifications émises sont inutiles. Il sait également que l’entier ne sera jamais supérieur à 10k, aussi les contrôles de débordement peuvent-ils être optimisés (non pas à cause de -Ofast étrange, mais à cause de la sémantique du langage (Rien d'autre ne change que var et ne peut y accéder, et ajouter jusqu'à 10k est sûr pour le type Int).

Le compilateur pourrait ne pas être en mesure de déballer le tableau ou les éléments du tableau, car ils sont passés à sort(), qui est une fonction externe et doit obtenir les arguments attendus. Cela nous obligera à utiliser les valeurs Int indirectement, ce qui le ralentirait un peu. Cela pourrait changer si la fonction générique sort() (et non à l'aide de plusieurs méthodes) était disponible pour le compilateur et intégrée.

C'est un tout nouveau langage (public) et il subit ce que je suppose, il y a beaucoup de changements, car il y a des gens (très) impliqués dans le Swift demandant un feedback et ils disent tous le langage. n'est pas terminé et changera .

Code utilisé:

import Cocoa

let Swift_start = NSDate.timeIntervalSinceReferenceDate();
let n: Int = 10000
let x = Int[](count: n, repeatedValue: 1)
for i in 0..n {
    for j in 0..n {
        let tmp: Int = x[j]
        x[i] = tmp
    }
}
let y: Int[] = sort(x)
let Swift_stop = NSDate.timeIntervalSinceReferenceDate();

println("\(Swift_stop - Swift_start)s")

P.S: Je ne suis pas un expert en Objective-C ni toutes les installations de Cocoa , Objective-C, ou les Swift runtimes. J'assume peut-être aussi des choses que je n'ai pas écrites.

107
filcab

J'ai décidé de regarder ça pour le plaisir, et voici les timings que je reçois:

Swift 4.0.2           :   0.83s (0.74s with `-Ounchecked`)
C++ (Apple LLVM 8.0.0):   0.74s

Swift

// Swift 4.0 code
import Foundation

func doTest() -> Void {
    let arraySize = 10000000
    var randomNumbers = [UInt32]()

    for _ in 0..<arraySize {
        randomNumbers.append(arc4random_uniform(UInt32(arraySize)))
    }

    let start = Date()
    randomNumbers.sort()
    let end = Date()

    print(randomNumbers[0])
    print("Elapsed time: \(end.timeIntervalSince(start))")
}

doTest()

Résultats:

Swift 1.1

xcrun swiftc --version
Swift version 1.1 (Swift-600.0.54.20)
Target: x86_64-Apple-darwin14.0.0

xcrun swiftc -O SwiftSort.Swift
./SwiftSort     
Elapsed time: 1.02204304933548

Swift 1.2

xcrun swiftc --version
Apple Swift version 1.2 (swiftlang-602.0.49.6 clang-602.0.49)
Target: x86_64-Apple-darwin14.3.0

xcrun -sdk macosx swiftc -O SwiftSort.Swift
./SwiftSort     
Elapsed time: 0.738763988018036

Swift 2.

xcrun swiftc --version
Apple Swift version 2.0 (swiftlang-700.0.59 clang-700.0.72)
Target: x86_64-Apple-darwin15.0.0

xcrun -sdk macosx swiftc -O SwiftSort.Swift
./SwiftSort     
Elapsed time: 0.767306983470917

Cela semble être la même performance si je compile avec -Ounchecked.

Swift 3.

xcrun swiftc --version
Apple Swift version 3.0 (swiftlang-800.0.46.2 clang-800.0.38)
Target: x86_64-Apple-macosx10.9

xcrun -sdk macosx swiftc -O SwiftSort.Swift
./SwiftSort     
Elapsed time: 0.939633965492249

xcrun -sdk macosx swiftc -Ounchecked SwiftSort.Swift
./SwiftSort     
Elapsed time: 0.866258025169373

Il semble y avoir eu une régression de performance de Swift 2.0 à Swift 3.0, et je constate également une différence entre -O et -Ounchecked pour la première temps.

Swift 4.

xcrun swiftc --version
Apple Swift version 4.0.2 (swiftlang-900.0.69.2 clang-900.0.38)
Target: x86_64-Apple-macosx10.9

xcrun -sdk macosx swiftc -O SwiftSort.Swift
./SwiftSort     
Elapsed time: 0.834299981594086

xcrun -sdk macosx swiftc -Ounchecked SwiftSort.Swift
./SwiftSort     
Elapsed time: 0.742045998573303

Swift 4 améliore encore les performances, tout en maintenant un écart entre -O et -Ounchecked. -O -whole-module-optimization n'a pas semblé faire la différence.

C++

#include <chrono>
#include <iostream>
#include <vector>
#include <cstdint>
#include <stdlib.h>

using namespace std;
using namespace std::chrono;

int main(int argc, const char * argv[]) {
    const auto arraySize = 10000000;
    vector<uint32_t> randomNumbers;

    for (int i = 0; i < arraySize; ++i) {
        randomNumbers.emplace_back(arc4random_uniform(arraySize));
    }

    const auto start = high_resolution_clock::now();
    sort(begin(randomNumbers), end(randomNumbers));
    const auto end = high_resolution_clock::now();

    cout << randomNumbers[0] << "\n";
    cout << "Elapsed time: " << duration_cast<duration<double>>(end - start).count() << "\n";

    return 0;
}

Résultats:

Apple Clang 6.

clang++ --version
Apple LLVM version 6.0 (clang-600.0.54) (based on LLVM 3.5svn)
Target: x86_64-Apple-darwin14.0.0
Thread model: posix

clang++ -O3 -std=c++11 CppSort.cpp -o CppSort
./CppSort     
Elapsed time: 0.688969

Apple Clang 6.1.

clang++ --version
Apple LLVM version 6.1.0 (clang-602.0.49) (based on LLVM 3.6.0svn)
Target: x86_64-Apple-darwin14.3.0
Thread model: posix

clang++ -O3 -std=c++11 CppSort.cpp -o CppSort
./CppSort     
Elapsed time: 0.670652

Apple Clang 7.0.

clang++ --version
Apple LLVM version 7.0.0 (clang-700.0.72)
Target: x86_64-Apple-darwin15.0.0
Thread model: posix

clang++ -O3 -std=c++11 CppSort.cpp -o CppSort
./CppSort     
Elapsed time: 0.690152

Apple Clang 8.0.

clang++ --version
Apple LLVM version 8.0.0 (clang-800.0.38)
Target: x86_64-Apple-darwin15.6.0
Thread model: posix

clang++ -O3 -std=c++11 CppSort.cpp -o CppSort
./CppSort     
Elapsed time: 0.68253

Apple Clang 9.0.

clang++ --version
Apple LLVM version 9.0.0 (clang-900.0.38)
Target: x86_64-Apple-darwin16.7.0
Thread model: posix

clang++ -O3 -std=c++11 CppSort.cpp -o CppSort
./CppSort     
Elapsed time: 0.736784

Verdict

Au moment d'écrire ces lignes, le tri de Swift est rapide, mais pas encore aussi rapide que celui de C++ lorsqu'il est compilé avec -O, avec les compilateurs et bibliothèques ci-dessus. Avec -Ounchecked, il semble être aussi rapide que C++ dans Swift 4.0.2 et Apple LLVM 9.0.0.

50
Learn OpenGL ES

De The Swift Programming Language :

Fonction de tri La bibliothèque standard de Swift fournit une fonction appelée sort, qui trie un tableau de valeurs d’un type connu, en fonction du résultat d’une fermeture de tri fournie. Une fois le processus de tri terminé, la fonction de tri renvoie un nouveau tableau du même type et de la même taille que l'ancien, avec ses éléments dans le bon ordre de tri.

La fonction sort a deux déclarations.

La déclaration par défaut qui vous permet de spécifier une clôture de comparaison:

_func sort<T>(array: T[], pred: (T, T) -> Bool) -> T[]
_

Et une deuxième déclaration qui ne prend qu'un seul paramètre (le tableau) et est "codée en dur pour utiliser le comparateur inférieur à".

_func sort<T : Comparable>(array: T[]) -> T[]

Example:
sort( _arrayToSort_ ) { $0 > $1 }
_

J'ai testé une version modifiée de votre code dans une cour de récréation avec la fermeture ajoutée afin de pouvoir surveiller la fonction de plus près, et j'ai constaté qu'avec n défini sur 1 000, la fermeture était appelée environ 11 000 fois.

_let n = 1000
let x = Int[](count: n, repeatedValue: 0)
for i in 0..n {
    x[i] = random()
}
let y = sort(x) { $0 > $1 }
_

Ce n'est pas une fonction efficace, je recommanderais d'utiliser une meilleure implémentation de la fonction de tri.

EDIT:

J'ai jeté un coup d'œil à la page wikipedia de Quicksort et j'ai écrit une implémentation pour le Swift. Voici le programme complet que j'ai utilisé (dans une cour de récréation)

_import Foundation

func quickSort(inout array: Int[], begin: Int, end: Int) {
    if (begin < end) {
        let p = partition(&array, begin, end)
        quickSort(&array, begin, p - 1)
        quickSort(&array, p + 1, end)
    }
}

func partition(inout array: Int[], left: Int, right: Int) -> Int {
    let numElements = right - left + 1
    let pivotIndex = left + numElements / 2
    let pivotValue = array[pivotIndex]
    swap(&array[pivotIndex], &array[right])
    var storeIndex = left
    for i in left..right {
        let a = 1 // <- Used to see how many comparisons are made
        if array[i] <= pivotValue {
            swap(&array[i], &array[storeIndex])
            storeIndex++
        }
    }
    swap(&array[storeIndex], &array[right]) // Move pivot to its final place
    return storeIndex
}

let n = 1000
var x = Int[](count: n, repeatedValue: 0)
for i in 0..n {
    x[i] = Int(arc4random())
}

quickSort(&x, 0, x.count - 1) // <- Does the sorting

for i in 0..n {
    x[i] // <- Used by the playground to display the results
}
_

En utilisant cela avec n = 1000, j'ai trouvé que

  1. quickSort () a été appelé environ 650 fois,
  2. environ 6000 échanges ont été effectués,
  3. et il y a environ 10 000 comparaisons

Il semble que la méthode de tri intégrée est (ou est proche de) tri rapide, et est vraiment lente ...

33
David Skrundz

À partir de Xcode 7, vous pouvez activer Fast, Whole Module Optimization. Cela devrait augmenter votre performance immédiatement.

enter image description here

18
Antoine

La performance de Swift Array revisitée:

J'ai écrit mon propre repère comparant Swift à C/Objective-C. Mon repère calcule les nombres premiers. Il utilise le tableau des nombres premiers précédents pour rechercher des facteurs premiers dans chaque nouveau candidat. Il est donc assez rapide. Cependant, cela fait des TONNES de lecture de tableau et moins d'écriture sur les tableaux.

À l’origine, j’avais fait ce repère contre Swift 1.2. J'ai décidé de mettre à jour le projet et de l'exécuter contre Swift 2.0.

Le projet vous permet de choisir entre l’utilisation de tableaux Swift normaux et l’utilisation de Swift tampons de mémoire non sécurisés à l’aide de la sémantique de tableau.

Pour C/Objective-C, vous pouvez choisir d’utiliser NSArrays ou des tableaux C mallocés.

Les résultats du test semblent être assez similaires avec l'optimisation la plus rapide et la plus petite du code ([-0s]) ou la plus rapide et agressive ([-0fast]).

Les performances de Swift 2.0 sont toujours horribles avec l'optimisation du code désactivée, alors que les performances de C/Objective-C ne sont que modérément plus lentes.

L’essentiel, c’est que les calculs basés sur des matrices C malloc sont les plus rapides, mais avec une marge modeste.

Swift avec des mémoires tampons non sûres prend environ 1,19 à 1,20 fois plus long que les baies C malloc en utilisant l'optimisation de code la plus petite et la plus rapide. la différence semble légèrement moins nette avec une optimisation rapide et agressive (Swift prend plus de 1,18x à 1,16x plus long que C.

Si vous utilisez des tableaux Swift réguliers, la différence avec C est légèrement plus grande. (Swift prend ~ 1,22 à 1,23 plus long.)

Les tableaux Swift ordinaires sont DRAMATICALLY plus rapides qu'ils ne l'étaient dans Swift 1.2/Xcode 6. Leurs performances sont si proches de celles de Swift, qui utilisent des mémoires tampon non sécurisées ne semble pas vraiment en valoir la peine, ce qui est grand.

BTW, Objective-C NSArray performance pue. Si vous envisagez d'utiliser les objets de conteneur natifs dans les deux langues, Swift est CONSIDÉRABLEMENT plus rapidement.

Vous pouvez consulter mon projet sur github à l'adresse suivante: SwiftPerformanceBenchmark

Il a une interface utilisateur simple qui rend la collecte de statistiques assez facile.

Il est intéressant de noter que le tri semble être légèrement plus rapide dans Swift que dans le C actuel, mais que cet algorithme de nombre premier est toujours plus rapide dans Swift.

11
Duncan C

Le problème principal qui est mentionné par d’autres, mais pas assez évoqué, c’est que -O3 ne fait rien du tout dans Swift (et ne l’a jamais fait), de sorte qu’il est effectivement non optimisé (-Onone).

Les noms des options ont changé au fil du temps, de sorte que certaines autres réponses ont des indicateurs obsolètes pour les options de construction. Les options actuelles correctes (Swift 2.2) sont les suivantes:

-Onone // Debug - slow
-O     // Optimised
-O -whole-module-optimization //Optimised across files

L’optimisation de tout le module a une compilation plus lente, mais peut être optimisée d’un fichier à l’autre, c’est-à-dire dans chaque cadre et dans le code de l’application, mais pas entre eux. Vous devriez utiliser ceci pour tout ce qui est critique en termes de performances)

Vous pouvez également désactiver les contrôles de sécurité pour encore plus de rapidité, mais avec toutes les assertions et conditions préalables non seulement désactivées, mais optimisées sur la base de leur exactitude. Si vous tapez une affirmation, cela signifie que vous avez un comportement indéfini. Utilisez avec une extrême prudence et uniquement si vous déterminez que le boost de vitesse en vaut la peine (en testant). Si vous le trouvez intéressant pour certains codes, je vous recommande de séparer ce code dans un cadre séparé et de ne désactiver que les contrôles de sécurité pour ce module.

8
Joseph Lord
func partition(inout list : [Int], low: Int, high : Int) -> Int {
    let pivot = list[high]
    var j = low
    var i = j - 1
    while j < high {
        if list[j] <= pivot{
            i += 1
            (list[i], list[j]) = (list[j], list[i])
        }
        j += 1
    }
    (list[i+1], list[high]) = (list[high], list[i+1])
    return i+1
}

func quikcSort(inout list : [Int] , low : Int , high : Int) {

    if low < high {
        let pIndex = partition(&list, low: low, high: high)
        quikcSort(&list, low: low, high: pIndex-1)
        quikcSort(&list, low: pIndex + 1, high: high)
    }
}

var list = [7,3,15,10,0,8,2,4]
quikcSort(&list, low: 0, high: list.count-1)

var list2 = [ 10, 0, 3, 9, 2, 14, 26, 27, 1, 5, 8, -1, 8 ]
quikcSort(&list2, low: 0, high: list2.count-1)

var list3 = [1,3,9,8,2,7,5]
quikcSort(&list3, low: 0, high: list3.count-1) 

Ceci est mon blog à propos de Quick Sort- exemple de Github Quick-Sort

Vous pouvez jeter un coup d'œil sur l'algorithme de partitionnement de Lomuto dans Partitionner la liste. Écrit en Swift

7
Abo3atef

Swift 4.1 introduit le nouveau mode d’optimisation -Osize.

Dans Swift 4.1, le compilateur prend désormais en charge un nouveau mode d'optimisation qui permet aux optimisations dédiées de réduire la taille du code.

Le compilateur Swift dispose de puissantes optimisations. Lors de la compilation avec -O, le compilateur tente de transformer le code afin qu'il s'exécute avec des performances optimales. Cependant, cette amélioration des performances d'exécution peut parfois être compensée par une taille de code accrue. Avec le nouveau mode d’optimisation -Osize, l’utilisateur a le choix de compiler pour une taille de code minimale plutôt que pour une vitesse maximale.

Pour activer le mode d'optimisation de la taille sur la ligne de commande, utilisez -Osize au lieu de -O.

Lectures supplémentaires: https://Swift.org/blog/osize/

4
casillas