Errores en el grupo de cadenas, o simplemente otra razón para pensar antes de internar instancias de cadenas en C #

Como desarrolladores de software, siempre queremos que el software que escribimos se ejecute rápido. Utilizando el algoritmo óptimo, paralelizando, aplicando diversas técnicas de optimización, recurriremos a todos los medios que conocemos para mejorar el rendimiento del software. Una de estas técnicas de optimización es la llamada internación de cuerdas. Le permite reducir la cantidad de memoria consumida por el proceso y también reduce significativamente el tiempo dedicado a comparar cadenas. Sin embargo, como en otras partes de la vida, es necesario observar la medida: no debe usar el internamiento en cada paso. El resto de este artículo le mostrará cómo puede quemarse y crear un cuello de botella no obvio para su aplicación en la forma del método 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