Introduction
J'ai ce favori de l'algorithme que j'ai fait il y a quelques temps je suis toujours écrit et ré-écrit dans de nouveaux langages de programmation, les plates-formes etc. comme une sorte de référence. Bien que mon principal langage de programmation C# je viens de littéralement copier-collé le code et changé la syntaxe légèrement, construit en Java et l'a trouvé pour exécuter 1000x plus rapide.
Le Code
Il y a un peu de code, mais je ne vais présenter cet extrait de ce qui semble être le principal problème:
for (int i = 0; i <= s1.Length; i++)
{
for (int j = i + 1; j <= s1.Length - i; j++)
{
string _s1 = s1.Substring(i, j);
if (tree.hasLeaf(_s1))
...
Les Données
Il est important de souligner que la chaîne s1 dans ce test particulier est de longueur 1 million de caractères (1 MO).
Les mesures
J'ai profilé mon exécution de code dans Visual Studio parce que je pensais que la manière dont je construis mon arbre ou de la façon dont je le traverse n'est pas optimal. Après examen des résultats, il apparaît que la ligne string _s1 = s1.Substring(i, j);
est accessible à plus de 90% du temps d'exécution!
Observations Supplémentaires
Une autre différence que j'ai remarqué, c'est que, bien que mon code est mono-thread Java gère pour l'exécuter à l'aide de tous les 8 cœurs (100% d'utilisation CPU) alors que même avec en Parallèle.For() et multi threading techniques de mon code C# parvient à utiliser de 35 à 40% au plus. Puisque l'algorithme évolue linéairement avec le nombre de cœurs (et la fréquence), j'ai compensé pour cela, et encore de l'extrait de code en Java exécute l'ordre de grandeur de 100 1000x plus rapide.
Le raisonnement
Je suppose que la raison pour laquelle ce qui se passe a à voir avec le fait que les chaînes de caractères en C# sont immuables sorte de Chaîne.Substring() a pour créer une copie et puisque c'est l'intérieur d'un ensemble de boucle avec un nombre d'itérations je présume que beaucoup de copie de ramassage des ordures et la collecte est en cours, cependant, je ne sais pas comment la sous-Chaîne est implémenté en Java.
Question
Quelles sont mes options à ce point? Il n'y a pas moyen de contourner le nombre et la longueur des chaînes (ce qui est déjà optimisé au maximum). Est-il une méthode que je ne connais pas (ou à la structure de données peut-être) qui pourrait résoudre ce problème pour moi?
Demande Minimale de mise en Œuvre (de commentaires)
J'ai laissé de côté la mise en œuvre du suffixe de l'arbre, qui est O(n) dans la construction et O(log(n)) dans la traversée de
public static double compute(string s1, string s2)
{
double score = 0.00;
suffixTree stree = new suffixTree(s2);
for (int i = 0; i <= s1.Length; i++)
{
int longest = 0;
for (int j = i + 1; j <= s1.Length - i; j++)
{
string _s1 = s1.Substring(i, j);
if (stree.has(_s1))
{
score += j - i;
longest = j - i;
}
else break;
};
i += longest;
};
return score;
}
Capture d'écran extrait de le profiler
Remarque cela a été testé avec string s1, la taille de 300.000 caractères. Pour une raison de 1 million de caractères ne se termine jamais en C# alors qu'en Java il faut seulement 0,75 secondes.. La mémoire consommée et le nombre de collectes ne semblent pas indiquer un problème de mémoire. Le pic a été d'environ 400 MO, mais compte tenu de l'énorme suffixe de l'arbre ce qui semble être normal. Pas bizarre ordures collecte des motifs tachetés soit.