Подтвердить что ты не робот

Штрих-интернирование в .NET Framework. Каковы преимущества и когда использовать интернирование

Я хочу знать процесс и внутренности интернирования строк , специфичных для .Net framework. Также хотелось бы узнать преимущества использования интернирования и сценариев/ситуаций, в которых мы должны использовать интернирование строк для повышения производительности. Хотя я изучал интернирование из книги CLR Джеффри Рихтера, но я все еще смущен и хотел бы узнать об этом более подробно.

[Редактирование], чтобы задать конкретный вопрос с примером кода ниже:

private void MethodA()
{
    string s = "String"; // line 1 - interned literal as explained in the answer        

    //s.intern(); // line 2 - what would happen in line 3 if we uncomment this line, will it make any difference?
}

private bool MethodB(string compareThis)
{
    if (compareThis == "String") // line 3 - will this line use interning (with and without uncommenting line 2 above)?
    {
        return true;
    }
    return false;
}
4b9b3361

Ответ 1

Interning - это внутренняя деталь реализации. В отличие от бокса, я не думаю, что есть какая-то польза от знания большего, чем то, что вы прочитали в книге Рихтера.

Преимущества микрооптимизации для интернирования строк вручную минимальны, поэтому обычно не рекомендуется.

Это, вероятно, описывает это:

class Program
{

    const string SomeString = "Some String"; // gets interned

    static void Main(string[] args)
    {


        try
        {
            var s1 = SomeString; // use interned string
            var s2 = SomeString; // use interned string
            var s = "String";
            var s3 = "Some " + s; // no interning 

            Console.WriteLine(s1 == s2); // uses interning comparison
            Console.WriteLine(s1 == s3); // do NOT use interning comparison


        }
        catch (Exception e)
        {

            Console.WriteLine(e.ToString());
        }


        Console.Read();
    }
}

Ответ 2

В общем, интернирование - это то, что происходит просто, автоматически, когда вы используете литеральные строковые значения. Interning обеспечивает преимущество наличия только одной копии литерала в памяти, независимо от того, как часто она используется.

При этом редко бывает, что есть причина, по которой вы ставите свои собственные строки, которые генерируются во время выполнения, или даже даже думают о том, что интернирование строк для нормальной разработки.

Есть потенциально некоторые преимущества, если вы собираетесь выполнять большую работу с сопоставлениями потенциально идентичных строк времени выполнения (поскольку интернирование может ускорить сравнение через ReferenceEquals). Тем не менее, это высокоспециализированное использование и потребует значительного количества профилирования и тестирования и не будет оптимизацией, которую я бы рассмотрел, если бы не была измеренная проблема на месте.

Ответ 3

Это "старый" вопрос, но у меня есть другой угол.

Если у вас будет много долгоживущих строк из небольшого пула, интернирование может улучшить эффективность памяти.

В моем случае я ставил другой тип объекта в статическом словаре, потому что они были повторно использованы, и это служило быстрым кэшем, прежде чем переносить их на диск.

Большинство полей в этих объектах являются строками, а пул значений довольно мал (во всяком случае, намного меньше числа экземпляров).

Если бы это были временные объекты, это не имело бы значения, потому что строковые поля часто собирали мусор. Но поскольку ссылки на них были сохранены, их использование памяти начало накапливаться (даже когда новые уникальные значения не добавлялись).

Таким образом, интернирование объектов существенно сократило использование памяти, а также интернировало их строковые значения во время их интернирования.

Ответ 4

Интернализация строк влияет на потребление памяти.

Например, если вы читаете строки и сохраняете их в списке для кеширования; и точно такая же строка происходит 10 раз, строка фактически хранится только один раз в памяти, если string.Intern используется. Если нет, строка хранится 10 раз.

В приведенном ниже примере версия string.Intern потребляет около 44 МБ, а без-версия (без комментирования) потребляет 1195 МБ.

static void Main(string[] args)
{
    var list = new List<string>();

    for (int i = 0; i < 5 * 1000 * 1000; i++)
    {
        var s = ReadFromDb();
        list.Add(string.Intern(s));
        //list.Add(s);
    }

    Console.WriteLine(Process.GetCurrentProcess().PrivateMemorySize64 / 1024 / 1024 + " MB");
}

private static string ReadFromDb()
{
    return "abcdefghijklmnopqrstuvyxz0123456789abcdefghijklmnopqrstuvyxz0123456789abcdefghijklmnopqrstuvyxz0123456789" + 1;
}

Интернализация также повышает производительность для equals-compare. Пример ниже интернальной версии занимает около 1 единицы времени, в то время как не-интерн занимает 7 единиц времени.

static void Main(string[] args)
{
    var a = string.Intern(ReadFromDb());
    var b = string.Intern(ReadFromDb());
    //var a = ReadFromDb();
    //var b = ReadFromDb();

    int equals = 0;
    var stopwatch = Stopwatch.StartNew();
    for (int i = 0; i < 250 * 1000 * 1000; i++)
    {
        if (a == b) equals++;
    }
    stopwatch.Stop();

    Console.WriteLine(stopwatch.Elapsed + ", equals: " + equals);
}

Ответ 5

Интернированные строки имеют следующие характеристики:

  • Две интернированные строки, которые идентичны, будут иметь один и тот же адрес в памяти.
  • Память, занятая интернированными строками, не освобождается, пока ваше приложение не завершится.
  • Интернирование строки включает вычисление хэша и поиск его в словаре, который потребляет циклы процессора.
  • Если несколько потоков внутри строк в одно и то же время будут блокировать друг друга, потому что доступ к словарю интернированных строк сериализуется.

Последствия этих характеристик:

  • Вы можете протестировать две интернированные строки для равенства, просто сравнив указатель адреса, который намного быстрее, чем сравнение каждого символа в строке. Это особенно верно, если строки очень длинные и начинаются с одних и тех же символов. Вы можете сравнивать интернированные строки с методом Object.ReferenceEquals, но безопаснее использовать оператор string ==, потому что он проверяет, являются ли строки первым в Интернете.

  • Если вы используете одну и ту же строку много раз в своем приложении, ваше приложение будет хранить только одну копию строки в памяти, уменьшая объем памяти, необходимый для запуска вашего приложения.

  • Если вы станете много разных строк, это будет выделять память для тех строк, которые никогда не будут освобождены, а ваше приложение будет потреблять все больший объем памяти.

  • Если у вас очень много интернированных строк, интернирование строк может стать медленным, и потоки будут блокировать друг друга при доступе к интернированному строковому словарю.

Вы должны использовать интернирование строк только в том случае, если:

  • Набор строк, которые вы интернируете, довольно мал.
  • Вы сравниваете эти строки много раз за каждый раз, когда вы ставите их.
  • Вам действительно нужна минимальная оптимизация производительности.
  • У вас нет много потоков, интенсивно интернирующих строки.

Ответ 6

Интерпретация строк - это оптимизация компилятором. Таким образом, вмешательство струн не в наших руках.

Я не знаю о преимуществах интернирования. Но определенно одна предосторожность может быть предоставлена: не используйте строку внутри вашей блокировки при многопоточности, а причина - STRING INTERNING. Ха-ха...