Armadilhas no pool de strings ou apenas outro motivo para pensar antes de internar instâncias de String em C #

Como desenvolvedores de software, sempre queremos que o software que escrevemos seja executado rapidamente. Usando o algoritmo ótimo, paralelizando, aplicando várias técnicas de otimização - vamos recorrer a todos os meios que conhecemos para melhorar o desempenho do software. Uma dessas técnicas de otimização é o chamado estágio de string. Ele permite que você reduza a quantidade de memória consumida pelo processo e também reduz significativamente o tempo gasto na comparação de strings. Porém, como em outras partes da vida, é preciso observar a medida - não se deve usar a internação em todas as etapas. O restante deste artigo mostrará como você pode se queimar e criar um gargalo não óbvio para seu aplicativo na forma do 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