Pièges dans le pool de chaînes, ou juste une autre raison de penser avant d'interner des instances de String en C #

En tant que développeurs de logiciels, nous voulons toujours que les logiciels que nous écrivons fonctionnent rapidement. En utilisant l'algorithme optimal, en parallélisant, en appliquant diverses techniques d'optimisation - nous recourrons à tous les moyens connus pour améliorer les performances du logiciel. L'une de ces techniques d'optimisation est le soi-disant internage de chaînes. Il vous permet de réduire la quantité de mémoire consommée par le processus et réduit également considérablement le temps passé à comparer les chaînes. Cependant, comme ailleurs dans la vie, il est nécessaire d'observer la mesure - vous ne devez pas utiliser l'internement à chaque étape. Le reste de cet article vous montrera comment vous pouvez vous brûler et créer un goulot d'étranglement non évident pour votre application sous la forme de la méthode String.Intern.





, , , C# . , – , , String, .





, , : , . . 100 %, . , 1 , , 4,7 ( 100 ). , , , , . , .





, String . -, ( String Pool). , . , , , . , , String. String, . , ( ). : String.Intern String.IsInterned.





, , String. , . IsInterned . , null ( ).





, , Intern. , , . , , , . , . , , .





. String.Equals:





public bool Equals(String value)
{
  if (this == null)
    throw new NullReferenceException();
 
  if (value == null)
    return false;
 
  if (Object.ReferenceEquals(this, value))
    return true;
  
  if (this.Length != value.Length)
    return false;
 
  return EqualsHelper(this, value);
}

      
      



EqualsHelper, , Object.ReferenceEquals . , Object.ReferenceEquals true ( ). , , EqualsHelper . Equals , , false ReferenceEquals .





, , Object.ReferenceEquals. . - , – . ReferenceEquals , .





, . , . .





, , , .





,

bug tracker' - , : ++ . , PVS-Studio . , , IncrediBuild. IncrediBuild , , . , , , , ( ), . .





, , PVS-Studio , IncrediBuild, , . – . , .





open source Unreal Tournament. IncrediBuild . 145 .





Unreal Tournament PVS-Studio, . CLMonitor.exe , Unreal Tournament Visual Studio. , , CLMonitor.exe, . PVS-Studio ThreadCount, CLMonitor.exe PVS-Studio.exe, ++ . PVS-Studio.exe , CLMonitor.exe.





: ThreadCount PVS-Studio, (145), , , 145 PVS-Studio.exe . IncrediBuild Build Monitor, , . - :





, : , , IncrediBuild . ...





,

, PVS-Studio.exe Build Monitor. IncrediBuild IncrediBuild. , , , : 182 8 50 IncrediBuild 145 . , 18 , 3,5 . Build Monitor. , :





, PVS-Studio.exe , - PVS-Studio.exe. . . . , , – IncrediBuild. - .





. , , . , CLMonitor.exe , . "" , CLMonitor.exe Visual Studio . Threads, 145 . , , :





....
return String.Intern(settings == null ? path
                                 : settings
                                 .TransformToRelative(path.Replace("/", "\\"),
                                                      solutionDirectory));
....
analyzedSourceFiles.Add( String.Intern(settings
                        .TransformPathToRelative(analyzedSourceFilePath, 
                                                 solutionDirectory))
                       );
....

      
      



? String.Intern. . , , CLMonitor.exe , PVS-Studio.exe. ErrorInfo, . , . , , ErrorInfo . .





, . , . - 145 String.Intern, LimitedConcurrencyLevelTaskScheduler CLMonitor.exe , PVS-Studio.exe, IncrediBuild . , , – PVS-Studio.exe ErrorInfo . , PVS-Studio.exe . , 145 .





ThreadCount , , String.Intern.





, CLMonitor.exe. : , PVS-Studio.exe ( , ).





, . Build Monitor - PVS-Studio.exe. 50 26, . , IncrediBuild 145 , 7 . , 3,5 .





String.Intern – , CoreCLR

, String.Intern, , - lock'. , String.Intern - , , , . , String.Intern reference source. , – Thread.GetDomain().GetOrInternString(str). , :





internal extern String GetOrInternString(String str);

      
      



. - . ? - CLR, .NET. , CoreCLR. , GetOrInternString :





STRINGREF *BaseDomain::GetOrInternString(STRINGREF *pString)

      
      



GetInternedString. , :





....
if (m_StringToEntryHashTable->GetValue(&StringData, &Data, dwHash))
{
  STRINGREF *pStrObj = NULL;
  pStrObj = ((StringLiteralEntry*)Data)->GetStringObject();
  _ASSERTE(!bAddIfNotFound || pStrObj);
  return pStrObj;
}
else
{
  CrstHolder gch(&(SystemDomain::GetGlobalStringLiteralMap()
                                   ->m_HashTableCrstGlobal));
  ....
  // Make sure some other thread has not already added it.
  if (!m_StringToEntryHashTable->GetValue(&StringData, &Data))
  {
    // Insert the handle to the string into the hash table.
    m_StringToEntryHashTable->InsertValue(&StringData, (LPVOID)pEntry, FALSE);
  }
  ....
}
....

      
      



else , , String ( GetValue) , false. , else. CrstHolder gch. CrstHolder :





inline CrstHolder(CrstBase * pCrst)
    : m_pCrst(pCrst)
{
    WRAPPER_NO_CONTRACT;
    AcquireLock(pCrst);
}

      
      



AcquireLock. . AcquireLock:





DEBUG_NOINLINE static void AcquireLock(CrstBase *c)
{
  WRAPPER_NO_CONTRACT;
  ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
  c->Enter();
}

      
      



, , – Enter. , , , , : "Acquire the lock". CoreCLR . , , , , , . CrstHolder, , m_StringToEntryHashTable->InsertValue.





, else. gch , ReleaseLock:





inline ~CrstHolder()
{
  WRAPPER_NO_CONTRACT;
  ReleaseLock(m_pCrst);
}

      
      



, . , 145 ( IncrediBuild), , , 144 , . Build Monitor.





, , . " ", . , , .





.





, : Ilya Gainulin. Pitfalls in String Pool, or Another Reason to Think Twice Before Interning Instances of String Class in C#.








All Articles