web-dev-qa-db-fra.com

Pourquoi la multiplication des matrices est plus lente que la mienne?

J'ai implémenté une multiplication matricielle avec boost::numeric::ublas::matrix (voir mon code boost complet et fonctionnel )

Result result = read ();

boost::numeric::ublas::matrix<int> C;
C = boost::numeric::ublas::prod(result.A, result.B);

et un autre avec l'algorithme standard (voir code standard complet ):

vector< vector<int> > ijkalgorithm(vector< vector<int> > A, 
                                    vector< vector<int> > B) {
    int n = A.size();

    // initialise C with 0s
    vector<int> tmp(n, 0);
    vector< vector<int> > C(n, tmp);

    for (int i = 0; i < n; i++) {
        for (int k = 0; k < n; k++) {
            for (int j = 0; j < n; j++) {
                C[i][j] += A[i][k] * B[k][j];
            }
        }
    }
    return C;
}

Voici comment je teste la vitesse:

time boostImplementation.out > boostResult.txt
diff boostResult.txt correctResult.txt

time simpleImplementation.out > simpleResult.txt
diff simpleResult.txt correctResult.txt

Les deux programmes lisent un fichier texte codé en dur qui contient deux matrices 2000 x 2000. Les deux programmes ont été compilés avec ces drapeaux:

g++ -std=c++98 -Wall -O3 -g $(PROBLEM).cpp -o $(PROBLEM).out -pedantic

J'ai 15 secondes pour mon implémentation et plus 4 minutes pour le boost -la mise en oeuvre!

edit: Après l'avoir compilé avec

g++ -std=c++98 -Wall -pedantic -O3 -D NDEBUG -DBOOST_UBLAS_NDEBUG library-boost.cpp -o library-boost.out

J'ai 28,19 secondes pour l'algorithme ikj et 60,99 secondes pour Boost . Boost est donc encore beaucoup plus lent.

Pourquoi le boost est-il tellement plus lent que mon implémentation?

46
Martin Thoma

Le ralentissement des performances de la version uBLAS peut s'expliquer en partie par le débogage des fonctionnalités de cette dernière, comme l'a souligné TJD.

Voici le temps pris par la version uBLAS avec le débogage:

real    0m19.966s
user    0m19.809s
sys     0m0.112s

Voici le temps pris par la version uBLAS avec le débogage désactivé (-DNDEBUG -DBOOST_UBLAS_NDEBUG drapeaux du compilateur ajoutés):

real    0m7.061s
user    0m6.936s
sys     0m0.096s

Donc, avec le débogage désactivé, la version uBLAS est presque 3 fois plus rapide.

La différence de performance restante peut être expliquée en citant la section suivante de BLAS FAQ "Pourquoi uBLAS est-il beaucoup plus lent que (atlas-) BLAS":

Un objectif de conception important des ublas est d'être aussi général que possible.

Cette généralité a presque toujours un coût. En particulier, le modèle de fonction prod peut gérer différents types de matrices, telles que des matrices clairsemées ou triangulaires. Heureusement, uBLAS fournit des alternatives optimisées pour la multiplication de matrice dense, en particulier axpy_prod et block_prod. Voici les résultats de la comparaison de différentes méthodes:

ijkalgorithm   prod   axpy_prod  block_prod
   1.335       7.061    1.330       1.278

Comme vous pouvez le voir à la fois axpy_prod et block_prod sont un peu plus rapides que votre implémentation. Mesurer uniquement le temps de calcul sans E/S, supprimer la copie inutile et choisir soigneusement la taille de bloc pour block_prod (J'en ai utilisé 64) peut rendre la différence plus profonde.

Voir aussi BLAS FAQ et Blas efficace et optimisation générale du code .

47
vitaut

Je crois que votre compilateur n'optimise pas suffisamment. Le code uBLAS fait un usage intensif des modèles et les modèles nécessitent une utilisation intensive des optimisations. J'ai exécuté votre code via MS VC 7.1 compilateur en mode release pour les matrices 1000x1000, cela me donne

10.064 S pour uBLAS

7.851 S pour le vecteur

La différence est toujours là, mais en aucun cas écrasante. Le concept de base d'uBLAS est l'évaluation paresseuse, donc prod(A, B) n'évalue les résultats qu'en cas de besoin, par ex. prod(A, B)(10,100) s'exécutera en un rien de temps, car seul cet élément sera réellement calculé. En tant que tel, il y a en fait aucun algorithme dédié pour la multiplication de la matrice entière qui pourrait être optimisé (voir ci-dessous). Mais vous pourriez aider un peu la bibliothèque en déclarant

matrix<int, column_major> B;

réduira le temps d'exécution à 4.426 s, ce qui bat votre fonction avec une main attachée. Cette déclaration rend l'accès à la mémoire plus séquentiel lors de la multiplication des matrices, optimisant l'utilisation du cache.

P.S. Après avoir lu la documentation uBLAS jusqu'à la fin;), vous auriez dû découvrir qu'il existe en fait une fonction dédiée pour multiplier les matrices entières à la fois. 2 fonctions - axpy_prod Et opb_prod. Donc

opb_prod(A, B, C, true);

même sur row_major B non optimisé, la matrice B s'exécute en 8.091 sec et est comparable à votre algorithme vectoriel

P.P.S. Il y a encore plus d'optimisations:

C = block_prod<matrix<int>, 1024>(A, B);

s'exécute dans 4.4 s, que B soit column_ ou row_ major. Considérez la description: "La fonction block_prod est conçue pour les matrices grandes denses." Choisissez des outils spécifiques pour des tâches spécifiques!

13
panda-34

J'ai créé un petit site Web Expériences de produit Matrix-Matrix avec uBLAS . Il s'agit d'intégrer une nouvelle implémentation du produit matrice-matrice dans uBLAS. Si vous avez déjà la bibliothèque boost, elle ne comprend que 4 fichiers supplémentaires. Il est donc à peu près autonome.

Je serais intéressé si d'autres pouvaient exécuter les benchmarks simples sur différentes machines.

2
Michael Lehn