9
votes

Tri par des blocs d'éléments avec STD :: Trier ()

J'ai une gamme d'arêtes, qui est définie comme une gamme de titres de type C de double, où tous les 4 doubles définissent un bord, comme celui-ci: xxx

donc donc je veux utiliser < Code> STD :: Trier () Pour trier la longueur de bord. S'il s'agissait d'un Struct Edge {Double X1, Y1, X2, Y2; }; EDGE * P; , je serais bon d'aller.

Mais dans ce cas, la double matrice a une taille de bloc qui n'est pas exprimée par le type de pointeur. Qsort () Vous permet de spécifier explicitement la taille du bloc, mais std :: Trier () Induit la taille de bloc par le type de pointeur.

Pour des raisons de performance (mémoire-utilisation et CPU), disons qu'il est indésirable de créer de nouveaux tableaux ou de transformer la matrice en quelque sorte. Pour des raisons de performance à nouveau, disons que nous voulons utiliser std :: try () au lieu de qsort () .

est-il possible de appeler std :: try () sans gaspiller un seul cycle de la CPU sur la transformation des données?

Approche possible:

Une approche évidente est d'essayer de forcer -Cast le pointeur: xxx

mais comment puis-je vous assurer que les données sont alignées correctement? En outre, comment puis-je vous assurer qu'il sera toujours aligné correctement sur toutes les plates-formes et toutes les architectures?

ou puis-je utiliser un Typef double [4] bord; ?


12 commentaires

Pourquoi STD :: Tri être plus rapide?


@Nick Johnson: Il utilise un meilleur algorithme autant que je sache.


Emperiquement, c'est, probablement parce qu'il peut aligner la comparaison. Cependant, sa peine de profiter d'un premier profilage de votre demande pour déterminer que le tri est un goulot d'étranglement qui mérite d'optimiser - le QSort fonctionne bien.


Avoir L Global n'affecte pas la sécurité du fil. En fait, comme vous pouvez le constater, QSort reçoit ce paramètre, mais il la copie à l'intérieur. En tant que côté, STD :: Trier est plus rapide que dans certains cas, mais ne sont sûrement pas sur des tableaux de CLARE C.


Remarque ajoutée sur qsort en raison d'un nombre de mauvaises réponses.


@Diego Sevilla: L affecte ma fonction de comparaison.


Pourquoi ne pouvez-vous pas copier votre tableau C dans un vecteur, trier, puis copier? Le tri est Nlogn de toute façon. Les 2 NS supplémentaires ne feront pas une différence notable.


Je veux utiliser seulement O (1) de mémoire supplémentaire.


REMARQUE SUR VOTRE PREMIER MISE À JOUR: Ne MPI_RECV prend-il un pointeur de mémoire tampon de sortie en tant que paramètre? Si tel est le cas, il n'y a aucune raison pour ne pas vous fournir un pointeur dans un vecteur plutôt qu'un tableau brut. Ce n'est pas que cela aide au problème réel pour essayer de le faire avec STD :: Trier, ce qui n'est bien sûr pas là où ils sont stockés, mais le fait que la taille des éléments triés n'est pas connue tant que le moment de l'exécution, alors STD :: Trier ne sait pas comment les déplacer.


@Alexandru J'ai le même problème, j'ai donc réécrit la question (espérons-le, mieux c'est maintenant) et ajouté une prime.


@sashoalm avez-vous envisagé d'utiliser une fonction de comparaison dans trier () fonction?


@sashoalm a jeté une réponse. La réinterprétation d'un tableau ne doit pas avoir de problèmes d'alignement - et devrait gérer correctement et échanger correctement.


10 Réponses :


0
votes
std::array< std::array<int, L>, N > array;
// or std::vector< std::vector<int> > if N*L is not a constant
std::sort( array.begin(), array.end() );

1 commentaires

Ensuite, utilisez qsort , à l'aide de STD :: Trier n'est pas un moyen plus rapide.



0
votes
#ifdef NDEBUG
  #define WAS_NDEBUG
  #undef NDEBUG
#endif
#define NDEBUG
#include <algorithm>
#ifdef WAS_NDEBUG
  #undef WAS_NDEBUG
#else
  #undef NDEBUG
#endif

1 commentaires

Je ne comprends pas. Comment cela gère-t-il la nature du bloc de la matrice?



2
votes

Vous pouvez utiliser un "itérateur de filet" pour cela. Un "itérateur Stride" enveloppe un autre itérateur et une taille d'une étape entière. Voici un croquis simple:

int array[N*L];
std::sort( make_stride_iter(array,L),
           make_stride_iter(array,L)+N );


4 commentaires

N'est-ce pas && A C ++ 0x Thingy? On dirait une bonne solution, je vais vérifier.


"N'est-ce pas && A c ++ 0x Thingy?" - Droite, mon erreur. Je l'ai changé.


Ouch, c'est un code de code. Boost :: iterator_Adaptor : boost.org/doc/libs/1_40_0/libs/iterator/doc/...


Ouais, en utilisant une bibliothèque d'adaptateur itérateur semble être une bonne idée. D'ailleurs, je pense que les itérateurs de Stride devraient faire partie de STD :: ou au moins Boost :: également. ;)



0
votes

Je ne sais pas si vous pouvez obtenir le même résultat sans beaucoup plus de travail. STD :: Trier () est effectué pour trier les séquences d'éléments définis par deux itérateurs d'accès aléatoire. Malheureusement, il détermine le type de l'élément de l'itérateur. Par exemple: xxx

triera tous les éléments de tableau . Le problème est qu'il suppose que l'inscription, l'incrément, la décrémentation et d'autres indexation les opérateurs de l'itérateur sur des éléments de la séquence. Je crois que la seule façon dont vous pouvez trier des tranches de la matrice (je pense que c'est ce que vous êtes après), est d'écrire un itérateur qui index basé sur l . C'est ce que Sellibitze a effectué dans le Stride_iterator (code> réponse / a>.


0 commentaires

1
votes

Je ne me souviens pas exactement comment faire cela, mais si vous pouvez fausse de fausses fonctions anonymes, vous pouvez créer une fonction COMP (L) qui renvoie la version de Comp pour les tableaux de longueur ... de cette façon de devenir Un paramètre, pas un global, et vous pouvez utiliser qsort. Comme les autres ont mentionné, sauf dans le cas où votre tableau est déjà trié, ou à l'envers ou quelque chose, qsort va être à peu près aussi rapide que tout autre algorithme. (Il y a une raison pour laquelle il s'appelle Quicksort après tout ...)


0 commentaires


1
votes

Il ne fait partie d'aucune norme ANSI, ISO ou POSIX, mais certains systèmes fournissent le qsort_r () code> fonction, ce qui vous permet de passer un paramètre de contexte supplémentaire à la fonction de comparaison. Vous pouvez ensuite faire quelque chose comme ceci: xxx pré>

alternativement, si vous n'avez pas qsort_r code>, vous pouvez utiliser le Callback (3) code> package de la bibliothèque FFCAlT pour créer des fermetures à l'exécution. Exemple: P>

#include <callback.h>
void comp_base(void *data, va_alist alist)
{
    va_start_int(alist);  // return type will be int

    int L = (int)data;
    const void *a = va_arg_ptr(alist, const void*);
    const void *b = va_arg_ptr(alist, const void*);

    // Now that we know L, compare
    int return_value = comp(a, b, L);

    va_return_int(alist, return_value);  // return return_value
}

...    

// In a function somewhere
typedef int (*compare_func)(const void*, const void*);

// Create some closures with different L values
compare_func comp1 = (compare_func)alloc_callback(&comp_base, (void *)L1);
compare_func comp2 = (compare_func)alloc_callback(&comp_base, (void *)L2);
...
// Use comp1 & comp2, e.g. as parameters to qsort
...
free_callback(comp1);
free_callback(comp2);


0 commentaires

0
votes

Arkadiy a la bonne idée. Vous pouvez trier en place si vous créez un tableau de pointeurs et triez cela: xxx

sortie: xxx


1 commentaires

remplacer int * buf = neuf int [nombre]; avec int * buf = neuf int [longueur]; La réponse d'Arkady était correcte, mais je voulais une solution sans un deuxième tableau. Je vais accepter sa réponse comme étant la meilleure solution 'Couse qu'il a proposé l'idée originale.



0
votes

Beaucoup de ces réponses semblent trop excédentaires. Si vous devez vraiment le faire de style C ++, en utilisant l'exemple de JMUCCHIELLO:

sort((Block<4> *)&array[0], (Block<4> *)&array[NN]);


2 commentaires

Cela nécessite que la longueur soit connue au moment de la compilation, cependant.


pas difficile à modifier autrement. De l'exemple, je l'ai basé sur, c'est.



2
votes

Pour la nouvelle question, nous devons passer dans trier () code> une sorte d'itérateur qui ne nous laissera pas seulement comparer les bonnes choses (c'est-à-dire veiller à prendre 4 étapes via notre double [] code> à chaque fois au lieu de 1) mais échangez également les bonnes choses (c.-à-d. Swap 4 double code> s au lieu d'un).

Nous pouvons accomplir les deux en réinterprétant simplement notre double Array comme s'il s'agissait d'un tableau de 4 doubles. Faire ceci: p> xxx pré>

ne fonctionne pas, car vous ne pouvez pas attribuer de tableau, et échange code> devra. Mais ce faisant: p> xxx pré>

ou, sinon c ++ 11: p> xxx pré>

satisfait aux deux exigences. Ainsi: p> xxx pré>

Si vous êtes préoccupé par l'alignement, peut toujours affirmer qu'il n'y a pas de remplissage supplémentaire: P>

static_assert(sizeof(Edge) == 4 * sizeof(double), "uh oh");


3 commentaires

Merci, je viens de voir la réponse. Pouvons-nous être sûrs qu'une telle structure est toujours alignée correctement? Est-il défini par la norme ou est-il laissé à la mise en œuvre? De plus, static_assert nécessite également C ++ 11, n'est-ce pas?


@sashoalm je le crois, bien que je ne puisse pas vous donner une citation. Edge doit avoir le même alignement que double - il ne devrait donc pas y avoir de rembourrage. Si ce n'est pas le cas, j'aimerais vraiment savoir pourquoi pas. Et vous pouvez utiliser boost_static_assert () à la place.


'#pragma pack' est supporté par VC ++ et GCC - Je ne l'utiliserais pas, si possible, si possible