10
votes

String.comparison Performances (avec garniture)

Je dois faire beaucoup de comparaisons de cordes insensibles à haute performance et réalisa que ma façon de le faire. Toolower (). Trim () était vraiment stupide dû faire toutes les nouvelles chaînes étant allouées

donc donc j'ai creusé autour d'un peu et cette façon semble préférable: p> xxx pré>

Le seul problème ici est que je veux ignorer les espaces d'avancement ou de fin, c'est-à-dire couper () mais si j'utilise la garniture que j'ai le même problème avec les allocations de chaîne. Je suppose que je pouvais vérifier chaque chaîne et voir si elle démarre ("") ou de bout en bout ("") et seulement puis de couper. Soit cela ou comprendre l'index, la longueur de chaque chaîne et passez à string.chaber remplaré p> xxx pré>

mais qui semble plutôt désordonnée et je dois probablement utiliser certains entiers si je Ne faites pas une énoncé très gros si-d'autre pour chaque combinaison de fuite et d'ébauches d'ébauches sur les deux chaînes ... de sorte que toutes les idées d'une solution élégante? P>

Voici ma proposition actuelle: P>

public bool IsEqual(string a, string b)
    {
        return (string.Compare(a, b, StringComparison.OrdinalIgnoreCase) == 0);
    }

    public bool IsTrimEqual(string a, string b)
    {
        if (Math.Abs(a.Length- b.Length) > 2 ) // if length differs by more than 2, cant be equal
        {
            return  false;
        }
        else if (IsEqual(a,b))
        {
            return true;
        }
        else 
        {
            return (string.Compare(a.Trim(), b.Trim(), StringComparison.OrdinalIgnoreCase) == 0);
        }
    }


8 commentaires

Qu'est-ce qui vous fait penser qu'il y a un problème? L'optimisation prématurée est une mauvaise idée - il n'est pas nécessaire d'optimiser tant que votre application devient "trop ​​lente". Entre-temps, concentrez-vous sur Clear Code sur un code rapide.


Pouvez-vous être sûr que le compilateur n'impose pas un tel cas pour vous de toute façon?


Je demanderais également si cela nécessite vraiment une micro-optimisation? Avez-vous vraiment un problème de performance dans ce domaine? J'imagine qu'il y ait d'autres domaines où vous pourriez avoir beaucoup d'amélioration de la performance


C'est pour un moteur de recherche sur un très grand ensemble de chaînes, donc je pense qu'il est pertinent d'optimiser dans ce cas. En outre, avoir une bonne méthode de comparaison de chaînes dans sa boîte à outils n'est pas une si mauvaise chose


@Anon: Je ne pense pas que ce soit une optimisation prématurée. S'il y a une grande quantité de chaînes, cela peut prendre beaucoup plus longtemps si vous créez de nouvelles instances de chaîne pour chaque comparaison. Il suffit de courir des tests et de voir par vous-même ...


Donc, c'est un "moteur de recherche"? Pourquoi ne l'avez-vous pas dit? Normaliser les cordes (garniture / minuscule) et mettre dans une table de hachage.


De plus, il semble y avoir un bogue dans votre code: deux chaînes peuvent différer de plus de 2 caractères, mais sont toujours identiques après la taille.


Ah oui, bien sûr, il y a des hashtables, mais ce n'est pas la partie indexée dont j'avais besoin pour autre chose, de toute façon je n'étais que curieux s'il y avait un moyen élégant de faire cela. Hmm ouais, je suppose que si vous envoyez des cordes avec beaucoup de blouse, soupir, bien, c'est une pensée de nuit


8 Réponses :


2
votes

Assurez-vous d'abord que vous devez vraiment optimiser ce code. Peut-être que la création de copies des chaînes n'affectera pas visiblement votre programme.

Si vous avez vraiment besoin d'optimiser, vous pouvez essayer de traiter les chaînes lorsque vous les stockez pour la première fois au lieu de les comparer lorsque vous les comparez (en supposant que cela se produise à différentes étapes du programme). Par exemple, stockez des versions coupées et minuscules des cordes, de sorte que lorsque vous les comparez, vous pouvez utiliser simplement une vérification de l'équivalence.


2 commentaires

Eh bien, il n'y a rien de mal à utiliser une méthode plus efficace dans ce cas. À l'aide de String.Compare n'est pas un piratage "intelligent", il est intégré pour comparer les chaînes également plus efficaces que l'appelant Toupper (). Tolower (). Il est également plus clair dans l'intention, donc je ne pense pas que vous puissiez faire une affaire de «optimisation prématurée» valide dans ce cas /


Je suppose que vous vouliez dire couper (). Tolower ()



2
votes

Vous ne pouvez pas simplement couper (et éventuellement le rendre minuscule) chaque chaîne exactement une fois (lors de l'obtention)? Faire plus de sons comme optimisation prématurée ....


1 commentaires

Bien sûr, dans certains cas, je pourrais le faire, juste intéressé à voir si on pouvait proposer une méthode d'usage générale optimisée pour le faire.



3
votes

J'utiliserais le code que vous avez xxx

et ajoutez tout .trim () appelle comme vous en avez besoin. Cela permettra d'enregistrer votre option initiale 4 cordes la plupart du temps ( .TObower (). Trim () et deux cordes tout le temps ( .TObower () ).

Si vous souffrez de problèmes de performance après cela, votre option "Messy" est probablement la meilleure mise.


3 commentaires

C'est intéressant. Mattias: Si la majorité de vos chaînes n'a pas besoin de l'appel de la garniture (), vous pourriez généralement le faire de cette façon et si les chaînes ne correspondent pas, retournez et essayez avec un appel de garniture (). "Vraiment" retourne qu'ils ne correspondent pas.


Hmm, dans ce cas, je suppose que je devrais exécuter des tests pour voir si les indicateurs ISPREFIX () / IssUFFIXIX () (quatre d'entre eux) prend plus ou moins de performances que tout simplement


Ah bien sûr! d'abord, faites le comparateur, puis faites la garniture Comparer (ou la méthode désordonnée) sinon 0, agréable



0
votes
  1. La chose est, si cela doit être fait, cela doit être fait. Je ne pense pas que l'une de vos différentes solutions fera une différence. Dans chaque cas, il doit y avoir un certain nombre de comparaisons pour trouver le blancheur ou le retirer.

    Apparemment, l'élimination de la blanchie fait partie du problème, de sorte que vous ne devriez pas vous inquiéter de cela.

  2. et la réduction de la chaîne d'une chaîne Avant de comparer, est un bogue si vous travaillez avec des caractères unicode et éventuellement plus lent que la copie d'une chaîne.


0 commentaires

0
votes

Les avertissements concernent l'optimisation prématurée sont corrects, mais je suppose que vous avez testé cela et constaté que beaucoup de temps sont en train de gaspiller des cordes. Dans ce cas, j'essaierais ce qui suit:

static void FindStartAndLength(string text, out int startIndex, out int length)
{
    startIndex = 0;
    while(char.IsWhiteSpace(text[startIndex]) && startIndex < text.Length)
        startIndex++;

    length = text.Length - startIndex;
    while(char.IsWhiteSpace(text[startIndex + length - 1]) && length > 0)
        length--;
}


0 commentaires

6
votes

Quelque chose comme ça devrait le faire: xxx

exemple: xxx

sortie: xxx

Vous devriez le profiler contre une simple garniture et comparer avec quelques données réelles, pour voir s'il y a vraiment une différence pour ce que vous allez l'utiliser pour.


2 commentaires

Intéressant, merci! Je ferai des comparaisons avec les différentes méthodes et voir lequel appartient le dessus


@konrad Quels ont été vos résultats de comparer cette solution avec une garniture?



0
votes

Vous pouvez implémenter votre propre stringcomparer code>. Voici une implémentation de base:

public class TrimmingStringComparer : StringComparer
{
    private StringComparison _comparisonType;

    public TrimmingStringComparer()
        : this(StringComparison.CurrentCulture)
    {
    }

    public TrimmingStringComparer(StringComparison comparisonType)
    {
        _comparisonType = comparisonType;
    }

    public override int Compare(string x, string y)
    {
        int indexX;
        int indexY;
        int lengthX = TrimString(x, out indexX);
        int lengthY = TrimString(y, out indexY);

        if (lengthX <= 0 && lengthY <= 0)
            return 0; // both strings contain only white space

        if (lengthX <= 0)
            return -1; // x contains only white space, y doesn't

        if (lengthY <= 0)
            return 1; // y contains only white space, x doesn't

        if (lengthX < lengthY)
            return -1; // x is shorter than y

        if (lengthY < lengthX)
            return 1; // y is shorter than x

        return String.Compare(x, indexX, y, indexY, lengthX, _comparisonType);
    }

    public override bool Equals(string x, string y)
    {
        return Compare(x, y) == 0;
    }

    public override int GetHashCode(string obj)
    {
        throw new NotImplementedException();
    }

    private int TrimString(string s, out int index)
    {
        index = 0;
        while (index < s.Length && Char.IsWhiteSpace(s, index)) index++;
        int last = s.Length - 1;
        while (last >= 0 && Char.IsWhiteSpace(s, last)) last--;
        return last - index + 1;
    }
}
  • Il n'est pas testé de manière approfondie et pourrait contenir des bugs li>
  • Les performances doivent encore être évaluées (mais c'est probablement mieux que d'appeler Cadmin code> et tolower code> de toute façon) li>
  • La méthode gethashcode code> n'est pas implémentée, alors ne l'utilisez pas comme une touche dans un dictionnaire li> ul> p>


0 commentaires

0
votes

Je remarque que votre première suggestion ne compare que pour l'égalité plutôt que pour le tri, qui permet d'économiser d'autres économies d'efficacité.

public static int TrimmedOrdinalIgnoreCaseHashCode(string str)
{
    //Higher CMP_NUM (or get rid of it altogether) gives
    //better hash, at cost of taking longer to compute.
    const int CMP_NUM = 12;
    if(str == null)
        return 0;
    int start = 0;
    int end = str.Length;
    while(start != end && char.IsWhiteSpace(str[start]))
        ++start;
    if(start != end)
        while(char.IsWhiteSpace(str[end - 1]))
            --end;

    int skipOn = (end - start) / CMP_NUM + 1;
    int ret = 757602046; // no harm matching native .NET with empty string.
    while(start < end)
    {
            //prime numbers are our friends.
        ret = unchecked(ret * 251 + (int)(char.ToLowerInvariant(str[start])));
        start += skipOn;
    }
    return ret;
}


0 commentaires