Как уменьшить объем памяти в приложениях .NET с интенсивным использованием строк?

У меня есть приложение, в памяти которого находится около 1 000 000 строк по соображениям производительности. Мое приложение потребляет ~ 200 МБ ОЗУ.

Я хочу уменьшить объем памяти, потребляемой строками.

Я знаю, что .NET представляет строки в кодировке UTF-16 (2 байта на символ). Большинство строк в моем приложении содержат чисто английские символы, поэтому хранить их в кодировке UTF-8 будет в 2 раза эффективнее, чем UTF-16.

Есть ли способ сохранить строку в памяти в кодировке UTF-8, разрешив при этом стандартные строковые функции? (Мои потребности включают в основном IndexOf с StringComparison.OrdinalIgnoreCase).


person DxCK    schedule 09.03.2012    source источник
comment
Как насчет использования массива байтов или List<byte>? Не уверен, насколько сложнее будет работать с этими объектами для ваших нужд.   -  person Jason Down    schedule 09.03.2012
comment
@DxCK, ты хочешь или нуждаешься? Разница важна, чтобы дать либо интересные ответы, либо практические.   -  person Alexei Levenkov    schedule 09.03.2012
comment
Вам обязательно нужно загружать в память все 1 000 000 строк? Можете ли вы предоставить более подробную информацию о том, что именно вы делаете со всеми этими строками в памяти?   -  person Dean Kuga    schedule 09.03.2012
comment
Я нигде не знаю об этом достаточно, чтобы написать фактический ответ, но если вы отключите интернирование строк (есть параметр сборки, который вы можете использовать), это должно остановить кэширование строк CLR. Это уменьшит память, если ваши строки не очень долговечны. Хотя будет еще хуже, если у вас будет куча одной и той же строки   -  person RichK    schedule 09.03.2012
comment
Почему проблема с 200 МБ, у вас проблема с нехваткой памяти или нехваткой памяти?   -  person Lasse V. Karlsen    schedule 09.03.2012
comment
@Алексей Левенков Хочу и нуждаюсь.   -  person DxCK    schedule 09.03.2012
comment
@ Дин К. Да, это в памяти из соображений производительности.   -  person DxCK    schedule 09.03.2012
comment
@Lasse V. Karlsen♦ Потому что это настольное приложение. Мои пользователи очень довольны производительностью, но не объемом памяти. Поэтому я пытаюсь улучшить его.   -  person DxCK    schedule 09.03.2012
comment
Но опять же, 200 МБ - это проблема? У ваших пользователей мало доступной памяти? Обратите внимание, что я также не говорю, что 200 МБ приемлемо, это зависит от приложения, но обычно, когда люди жалуются на использование памяти и приложений, они не считают, что вся эта память доступна только по одной причине; чтобы приложения работали быстро!   -  person Lasse V. Karlsen    schedule 09.03.2012
comment
Мне нравится сравнивать использование памяти с использованием места в гараже. Если у вас большой гараж на 10 машин, зачем вы придираетесь к квадратному метру на скамейке в углу?   -  person Lasse V. Karlsen    schedule 09.03.2012
comment
@LasseV.Karlsen - Если вы позаботитесь о центах, доллары позаботятся о себе сами.   -  person daniloquio    schedule 09.03.2012
comment
@Lasse V. Karlsen♦ Мои пользователи, включая всех домашних пользователей, но не ограничиваясь ими.   -  person DxCK    schedule 09.03.2012
comment
@DxCK в такой ситуации я бы использовал базу данных в памяти и SQL для поиска строк и т. Д. - это вариант для вас?   -  person Yahia    schedule 09.03.2012
comment
Если пользователям не нужно будет устанавливать компоненты на свою машину, то да, это вариант. Это может повлиять на функциональность и производительность всего приложения. нужно сделать POC этого. Есть ли рекомендации по такой БД?   -  person DxCK    schedule 09.03.2012
comment
@DxCK Неясно, используете ли вы конфигурацию сборки x86 или AnyCPU? Если вы ориентируетесь на x86, а не на 64-разрядную версию, вы можете значительно сократить использование памяти.   -  person cspolton    schedule 09.03.2012
comment
@DxCK Кроме того, рассматривали ли вы возможность сжатия в памяти строк размером более 1 КБ? Я читал статью, в которой упоминалось, что Stack Overflow сжимает содержимое своих кешей. См. hanselman.com/blog/.   -  person cspolton    schedule 09.03.2012
comment
@Spolto Спасибо за комментарий. На данный момент я ориентируюсь на AnyCPU, потому что хочу работать на любом компьютере с Windows (x86 или x64) и использовать преимущества x64 в производительности и масштабе памяти, когда они доступны. Ориентация на x86, безусловно, уменьшит объем памяти, но строки по-прежнему остаются доминирующими потребителями памяти.   -  person DxCK    schedule 09.03.2012
comment
200-мегабайтные строки звучат как множество символов. Это для файлов журнала или больших файлов XML? (Если это так, то клиент, вероятно, должен запускать приложение на мощном рабочем столе или сервере)   -  person Chris S    schedule 09.03.2012
comment
Я вижу, как люди пытаются угадать, что именно делает мое приложение... поэтому, если хотите, можете скачать и посмотреть сами: master-seeker.com   -  person DxCK    schedule 09.03.2012
comment
Ненавижу быть вестником плохих новостей DxCK, но все уже делают это с использованием индекса NTFS в Windows :) Тем не менее, это все еще интересный вопрос.   -  person Chris S    schedule 09.03.2012
comment
@Chris S Я все знаю. Но есть некоторые отличия. Чтобы увидеть некоторые из них, просто попробуйте поискать kernel32.dll в обоих приложениях (не параллельно!), мое приложение даст больше результатов. Кроме того, мое приложение поддерживает FAT32, отображает размер папок и может быстро сортировать по размеру и дате и времени. В любом случае, я не знаю, подходящее ли это место для обсуждения этого.   -  person DxCK    schedule 10.03.2012
comment
Являются ли все строки уникальными значениями?   -  person Mike    schedule 10.03.2012
comment
@Майк, большинство из них уникальны, но это хорошо. Я попробую также некоторую дедупликацию. Спасибо!   -  person DxCK    schedule 10.03.2012


Ответы (5)


К сожалению, вы не можете изменить внутреннее представление строки .Net. Я предполагаю, что CLR оптимизирована для многобайтовых строк.

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

Тем не менее, взгляните на некоторые соображения здесь. Если бы я был вами, как только вы установили, что прироста памяти вам будет достаточно, попробуйте написать свой собственный «строковый» класс, который использует кодировку ASCII. Этого, наверное, будет достаточно.

ОБНОВИТЬ:

Подробнее о деньгах вы должны проверить этот пост, "Of memory and strings", написанный легендой StackOverflow Джоном Скитом, который решает проблему, с которой вы столкнулись. Извините, что не упомянул об этом сразу, мне потребовалось некоторое время, чтобы найти точный пост от Джона.

person Bruno Brant    schedule 09.03.2012

Есть ли способ сохранить строку в памяти в кодировке UTF-8, разрешив при этом стандартные строковые > функции? (Мои потребности включают в основном IndexOf с StringComparison.OrdinalIgnoreCase).

Вы можете сохранить как массив байтов и предоставить свою собственную реализацию IndexOf (поскольку преобразование обратно в строку для IndexOf, вероятно, будет огромным ударом по производительности). Используйте для этого функции System.Text.Encoding (лучше всего было бы выполнить шаг сборки для преобразования в байты, а затем прочитать массивы байтов с диска - только преобразовать обратно в строку для отображения, если это необходимо).

Вы можете хранить их в библиотеке C/C++, позволяя использовать однобайтовые строки. Вы, вероятно, не захотите упорядочивать их обратно, но, возможно, вы можете просто упорядочить результаты (я предполагаю, что здесь происходит какой-то поиск) без особого удара по производительности. C++/CLI может упростить эту задачу (благодаря возможности писать поисковый код на C++/CLI, а строку «база данных» — на C++).

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

person Mark Brackett    schedule 09.03.2012
comment
Как можно реализовать сравнение символов IgnoreCase? Есть ли какая-либо библиотека/представление UTF-8, доступная в C/C++? - person DxCK; 09.03.2012
comment
@DxCK проблема в том, что если вы ограничиваете себя 8 битами, вы не поддерживаете большую часть языков, используемых в мире, даже с C ++ и UTF8. - person Chris S; 09.03.2012
comment
@Chris S Как кодировка UTF8 ограничивает язык? - person DxCK; 10.03.2012
comment
Я предлагаю создать простую таблицу поиска с предварительно вычисленным преобразованием регистра - поскольку вы будете кодировать до 8 бит, вам потребуется таблица 256 записей, и вы можете преобразовать, просто выполнив поиск (например, byte lowChar = _lowcaseTable[upperChar];) - person ; 10.03.2012
comment
@DxCK - это звучит как еще один ТАКОЙ вопрос. ;) У Boost есть библиотека строк, есть различные API-интерфейсы Windows и т. д. Если вы пойдете по маршруту массива байтов, я думаю, вам нужно будет предоставить свой собственный. Боюсь, это выходит за рамки моих ограниченных знаний о Unicode, хотя я полагаю, что вы могли бы сделать хуже, чем простое сравнение ASCII для чистых последовательностей ASCII и полагаться на BCL для сложных сравнений Unicode. - person Mark Brackett; 10.03.2012
comment
@sgorozco - я думаю, вы путаете UTF-8 (который является Unicode) с обычным ASCII. UTF-8 хранит символы ASCII как один байт, но имеет переменную ширину для хранения остальной части Unicode. - person Mark Brackett; 10.03.2012
comment
Другими словами, UTF8 отлично подходит для английского и европейских языков, но ограничение ваших строк до 8 бит для оптимизации памяти будет означать, что индуистский, китайский языки не поддерживаются. - person Chris S; 10.03.2012
comment
@ChrisS: Опять же, UTF8 не ограничивает 8-бит. Он просто оптимизирует английские символы (используя 1 байт) за счет нескольких 3-байтовых последовательностей (которые UTF16 не использует). В остальном он очень похож на UTF16. - person Mark Brackett; 10.03.2012
comment
@MarkBrackett Я знаю это, я думаю, что я пытался донести до DxCK то, что вам, вероятно, придется вернуться к string в какой-то момент, если вы не проверите кодировку и не сделаете то, что вы упомянули, напишите свой собственный indexof - person Chris S; 13.03.2012

Что, если вы сохраните его как байтовый массив? Просто восстановите строку, когда вам нужно выполнить с ней какие-то операции. Я бы создал класс для установки и получения строк, которые внутренне сохраняют его как bytearrays.

в байтовый массив:

string s = "whatever";
byte[] b = System.Text.Encoding.UTF8.GetBytes(s);

нанизывать:

string s = System.Text.Encoding.UTF8.GetString(b);
person SpoBo    schedule 09.03.2012
comment
Я пробовал это. Преобразование обратно в String почти не влияет на производительность: выделение памяти, преобразование из UTF-8 в UTF-16, затем GC. за 1 000 000 строк это очень ощутимые затраты. - person DxCK; 09.03.2012
comment
@DxCK, затем GC - что ты имеешь в виду? - person Henk Holterman; 09.03.2012
comment
ну что вы хотите... производительность или меньшую занимаемую площадь? :) Вашему приложению постоянно нужна каждая строка? Если нет, то, возможно, сохраните только те строки, которые давно не использовались. Создайте класс, который выполняет какой-то внутренний «сбор памяти» вместо сбора мусора. - person SpoBo; 09.03.2012
comment
Я предполагаю, что массив байтов не годится, так как ему нужно искать строки - person Chris S; 09.03.2012
comment
хорошо, вы можете использовать массив байтов и иметь хорошую производительность, если вы переписываете класс String, но с предпочитаемой вами кодировкой char. Ура, помните структуры данных. - person Patrick Lorio; 10.03.2012
comment
جب یہ اردو ہے کیا ہوتا ہے؟ (Что происходит, когда это урду?) - person Chris S; 10.03.2012

попробуйте использовать базу данных в памяти в качестве «хранилища» и SQL для взаимодействия с данными... Например, SQLite можно развернуть как часть вашего приложения (состоит всего из 1-2 DLL, которые можно поместить в одну и ту же папку). как ваше приложение)...

person Yahia    schedule 09.03.2012

Что, если вы создадите свой собственный строковый класс UTF-8 (UTF8String?) и предоставите неявное приведение к String? Вы пожертвуете скоростью ради памяти, но это может быть то, что вы ищете.

person itsme86    schedule 09.03.2012
comment
Я пробовал это. Преобразование обратно в String почти не влияет на производительность. преобразование из UTF-8 в UTF-16, затем GC. за 1 000 000 строк это очень ощутимые затраты. - person DxCK; 09.03.2012