Камень преткновения — GC


Все managed языки такие как Java или C# имеют один существенный недостаток — безусловное автоматическое управление паматью. Казалось бы, именно это и является преимуществом managed языков. Помните, как мы барахтались с dandling-указателями, не понимая, куда утекают драгоценные 10KB в час, заставляя рестартать наш любимый сервер раз в сутки? Конечно, Java и C# (и иже с ними) на первый взгляд разруливают ситуацию в 99% случаев.

Так-то оно так, только вот есть одна проблемка: как быть с большим кол-вом объектов, ведь в том же .Net никакой магии нет. CLR должен сканировать огромный set объектов и их взаимных ссылок. Это проблема частично решается путём введения поколений. Исходя из того, что большинство объектов живёт недолго, мы высвобождаем их быстрее и поэтому не надо каждый раз ходить по всем объектам хипа.

Но проблема всё равно есть в тех случаях, когда объекты должны жить долго. Например, кэш. В нём должны находиться миллионы объектов. Особенно, учитывая возрастание объемов оперативки на типичном современном серваке. Получается, что в кэше потенциально можно хранить сотни миллионов бизнес-объектов (например, Person с дюжиной полей) на машине с 64GB памяти.

Однако на практике это сделать не удаётся. Как только мы добавляем первые 10 миллионов объектов и они “устаревают” из первого поколения во второе, то очередной полный GC-scan “завешивает” процесс на 8-12 секунд, причём эта пауза неизбежна, т.е. мы уже находимся в режиме background server GC и это только время “stop-the-world”. Это приводит к тому, что серверная апликуха просто “умирает” на 10 секунд. Более того, предсказать момент “клинической смерти” практически невозможно.
Что же делать? Не хранить много объектов долго?

Зачем


Но мне НУЖНО хранить очень много объектов долго в конкретной задаче. Вот например, я храню network из 200 миллионов улиц и их взаимосвязей. После загрузки из flat файла моё приложение должно просчитать коэффициенты вероятностей. Это занимает время. Поэтому я это делаю сразу по мере загрузки данных с диска в память. После этого мне нужно иметь object-graph, который уже прекалькулирован и готов “к труду и обороне”. Короче, мне нужно хранить резидентно около 48GB данных в течении нескольких недель при этом отвечаю на сотни запросов в секунду.

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

Как


Вот мы и решили сделать свой memory manager и назвали его “Pile” (куча). Ибо никак не обойти “калечность” managed memory model. Unmanaged memory ничем не спасает. Доступ к ней занимает время на проверки, которые “убивают” скорость и усложняют весь дизайн. Ни .Net, ни Java не умееют работать в “нормальном” режиме с кусками памяти, которые не на хипе.

Что мы сделали? Наш memory manager — это абсолютно 100% managed код. Мы динамически выделяем массивы byte, которые мы называем сегментами. Внутри сегмента у нас есть указатель — обычный int. И вот мы получаем PilePointer:
/// <summary>
/// Represents a pointer to the pile object (object stored in a pile).
/// The reference may be local or distributed in which case the NodeID is>=0.
/// Distributed pointers are very useful for organizing piles of objects distributed among many servers, for example
///  for "Big Memory" implementations or large neural networks where nodes may inter-connect between servers.
/// The CLR reference to the IPile is not a part of this struct for performance and practicality reasons, as
///  it is highly unlikely that there are going to be more than one instance of a pile in a process, however
///  should more than 1 pile be allocated than this pointer would need to be wrapped in some other structure along
///   with source IPile reference
/// </summary>
 public struct PilePointer : IEquatable<PilePointer>
 {
    /// <summary>
    /// Distributed Node ID. The local pile sets this to -1 rendering this pointer as !DistributedValid
    /// </summary>
    public readonly int NodeID;

    /// <summary>
    /// Segment # within pile
    /// </summary>
    public readonly int Segment;

    /// <summary>
    /// Address within the segment
    /// </summary>
    public readonly int Address;
…………………………………………………………………
}


Обратите внимание на NodeID, о нём ниже. Получить PilePointer можно следующим образом:

var obj = new MyBusinessType();
var pilePointer = Pile.Put(obj);
…………………………………………
// где-то в другом месте, где есть поинтер
var originalObj = Pile.Get(pilePointer);


Мы получим копию оригинального объекта, который мы погрузили в Pile с помощью Put(), либо PileAccessViolation, если pointer неправильный.

Pile.Delete(pilePointer) 

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

Вопрос: как это сделано и что мы храним в byte[], ведь мы не можем хранить объекты CLR с реальными поинтерами, тогда они запутывают GC. Нам как раз надо обратное — хранить что-то в нашем формате, убрав managed references. Таким образом, мы сможем хранить данные, а GC не будет знать, что это объекты, ну и не будет их визитировать. Это возможно сделать через сериализацию. Конечно, имеются в виду не встроенные сериализаторы .Net (такие как BinaryFormatter), а наши родные в NFX.

PilePointer.NodeID позволяет “размазывать” данные по распределённым “кучам”, так как он идентифицирует узел в когорте distributed pile.

А теперь главный вопрос. Зачем это всё надо, если “под капотом” используется сериализация и это медленно?

Скорость


Реально это работает так: объект < 300 байт, погруженный в byte[] с помошью NFX Slim сериализации, занимает в среднем меньше на 10-25% места чем native объект CLR в памяти. Для больших объектов эта разница стремится к нулю. Почему так получается? Дело в том, что NFX.Serialization.Slim.SlimSerializer использует UTF8 для строк и variable length integer encoding + не нужен 12+ байт CLR header. В итоге камнем преткновения становится скорость сериализатора. SlimSerializer “держит” феноменальную скорость. На одном ядре Intel I7 Sandy Bridge с частотой 3GHz мы превращаем 440 тысяч PilePointer’ов в объект в секунду. Каждый объект в этом тесте имеет 20 заполненных полей и занимает 208 байт памяти. Вставка объектов в Pile одним ядром 405 тысяч в секунду. Такая скорость достигается за счёт динамической компиляции expression trees для каждого сериализируемого объекта в pile-сегмент. В среднем SlimSerializer работает раз в 5 быстрее, чем BinaryFormatter, хотя для многих простых типов этот коэффициент достигает 10. С точки зрения спэйса SlimSerializer пакует данные в 1/4 — 1/10 того, что делает BinaryFormatter. Ну и самое главное. SlimSerializer НЕ ТРЕБУЕТ специальной разметки полей в объектах, с которыми мы работаем. Т.е. хранить можно всё, что угодно, кроме delegates.

Многопоточный тест на вставку данных стабильно держит больше 1 миллиона транзакций в секунду на CoreI7 3GHz.
Ну и теперь самое главное. Аллокировав 300.000.000 объектов в нашем процессе полный GC занимает менее 30 миллисекунд



Итоги


Технология NFX.ApplicationModel.Pile позволяет избежать непредсказуемых задержек, вызванных сборщиком мусора GC, держа сотни миллионов объектов резидентно в памяти в течении длительного времени (недели), обеспечивая скорость доступа выше, чем “out-of-process” решения (такие как MemCache, Redis et.al).

Pile основан на специализированном memory manager’е, который аллокирует большие byte[] и распределяет память для приложения. Погруженный в Pile объект идентифицируется структурой PilePointer, ктр. занимает 12 байт, что способствует созданию эффективных object graphs, где объекты взаимно ссылаются.

Get the code:

NFX GitHub

Комментарии (81)


  1. kekekeks
    30.04.2015 20:23
    +2

    А где код-то?


  1. itadapter Автор
    30.04.2015 20:37
    +3

    1. kekekeks
      30.04.2015 21:01
      +1

      Я, видимо, попал в момент между публикацией статьи и заливкой исходников.


  1. eosunknown
    30.04.2015 21:06
    +3

    Бывает же кому-то не скучно O_o


  1. Neris
    30.04.2015 21:16

    Это приводит к тому, что серверная апликуха просто “умирает” на 10 секунд. Более того, предсказать момент “клинической смерти” практически невозможно.

    Вообще-то, можно. И в вашем случае, как раз и нужно.


    1. MarcusAurelius
      30.04.2015 21:34
      +2

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


      1. Neris
        30.04.2015 21:38
        +3

        Там как раз пример есть. Поднимается два инстанса приложения. GC паузы мониторятся. Как только на каком-то инстансе запускается сборка мусора, он исключается из балансировщика и спокойно себе собирает мусор пока запросы обрабатывает второй инстанс.


        1. itadapter Автор
          30.04.2015 21:42

          у меня в много objects- они все volatile, етот state стрoился 2 недели, о каком переклучении процесса идет реч? реконструироват 500.000.000 objects for 2 weeks just to collect 50 Mb of trash?


        1. MarcusAurelius
          30.04.2015 21:43
          +1

          Ну это же ужасно, в 2 раза больше памяти, а вдруг будет наложение, так что 3 раза хранить. А потом когда разлипнет процесс, то что, эту память нужно синхронизировать между процессами, она же изменится.


          1. itadapter Автор
            30.04.2015 21:48
            +3

            то что мы сделали основано исклучително на ПРАКТИЧЕСКОМ опыте.
            Перемещать десятки миллионов записеи в раме очень медленно. Между машинами — нереално.
            Я молчу о сотнях миллионов записеи. А если записи связани друг с другом — то ето вообше коллапс.
            It takes DAYS to move billions of rows between nodes


            1. MarcusAurelius
              30.04.2015 21:55
              +1

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


            1. Neris
              30.04.2015 21:55

              Да, простите, в вашем случае не разобрался. Просто глаз зацепился за невозможность сосуществования со сборками мусора.


              1. MarcusAurelius
                30.04.2015 22:02
                +9

                Просто в статье написано кэш, а это подразумевает, что его можно потерять и восстановить. Тут скорее модель, основная копия объектов, где они живут как в бездисковой СУБД.


            1. kekekeks
              30.04.2015 22:26
              +1

              Перемещать десятки миллионов записеи в раме очень медленно. Между машинами — нереално.

              Ну так вы сделайте через event sourcing с бродкастом событий между инстансами через какой-нибудь EventStore, будет постоянно живая полная реплика, проблему-то нашли.


              1. Master_Dante
                01.05.2015 13:22

                Что вы имеете ввиду? Если я правильно понял, то можно пробрасывать данные между процессами IIS?


              1. leventov
                02.05.2015 16:07

                Вы так это описываете, как будто пишете такие системы по три раза перед завтраком.


                1. kekekeks
                  02.05.2015 16:09

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


                  1. itadapter Автор
                    02.05.2015 16:58
                    +1

                    а как же моногофазовый пахос, или на многих нодах будет тогда мусор?
                    вы поймите — локалный рам всегда быстрее чем network, даже 40 Гбит. + координация ИП ядра на послки пакетов, DMA, IRQ

                    Загрузите код и сделайте тест


          1. Neris
            30.04.2015 21:54

            Горизонтальное масштабирование. Нагрузка делится равномерно на несколько инстансов. К тому же, получаем бОльшую отказоустойчивость (если какой-то инстанс выйдет из строя, его заменят другие). Аппаратные ресурсы при этом, обычно, разумная жертва в угоду производительности.
            Синхронизация это уже отдельная задача архитектуры. Полезно почитать.


            1. itadapter Автор
              30.04.2015 21:59
              +3

              это ужe на 1м ноде. в кластере у нас сотни миллиардов объектов.
              как раз это и позволиает имет каш хит rate > 25% очен дешево ЛОКАЛьНО


    1. itadapter Автор
      30.04.2015 21:39
      +2

      попробуите хранить устоичиво 500.000.000 objects таких:
      person
      {
      GDID key, //this is 12 bytes
      GDID user, //12 bytes
      string msg,
      DateTime ts,
      GDID? ref //1 or 13 bytes
      }
      и посмотрите что будет с вашим сервером


      1. kekekeks
        30.04.2015 22:31
        +4

        Массивы структур использовать не пробовали? С указанием строк как оффсета в массиве char-ов. Получилось бы, внезапно, то же самое, но куда эффективнее как с точки зрения отсутствия расходов на сериализацию, так и в плане GC pressure (не нужно собирать десериализованные экземпляры).


        1. itadapter Автор
          30.04.2015 22:47
          +2

          а что если НЕИЗВЕСТНО какие типы данних надо хранить? например — в ДБ каше мы храним бизнесс данные — какои их тип — неизвестно.
          это может быть листы сложных объектов, транзакции. Хранить все во флат byte[] — это значит не исползоват C# objects как таковои.
          Pile — ето БАЗА ДАННИХ/warehouse of objects в памяти


          1. kekekeks
            30.04.2015 22:49
            +2

            Ну так сделайте обобщённые классы пулов на generic-типах, пока типы будут соответствовать набору правил (нет ссылок на reference-типы), всё будет работать на отлично.


          1. leventov
            30.04.2015 23:32
            +3

            Кодогенерация в рантайме?


        1. VladVR
          03.05.2015 00:41

          А как работать с массивом char-ов? Имеется ввиду создавать объект string перед использованием?

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

          Еще не представляю как делать освобождение таких строк, если исходная структура больше не нужна, и ее можно удалить из массива или перезатереь другой, то как можно узнать, что больше никто не держит копию этой структуры? В сценарии «загрузить большой файл в память и с ним работать» это конечно неактуально, но наверняка найдутся другие сценарии. Разве что заботиться не об удалении единичных экземпяров, а об дропе некого большого блока «устаревшего кэша».


      1. musuk
        01.05.2015 07:37
        +3

        А почему не хранить такое в какой-нибудь редиске или другой No-SQL? Зачем вообще нужно хранить много данных в процессе?


        1. kekekeks
          01.05.2015 13:55
          +2

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


          1. musuk
            01.05.2015 14:25

            Так для этих целей графовые СУБД есть. Хотя я понял о чём вы, это как раз о том случае, когда ты сам реализуешь СУБД.


  1. Vindicar
    30.04.2015 21:26
    +18

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

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


    1. itadapter Автор
      30.04.2015 21:36
      -3

      деиствительно смешно, но нам ето надо чтобы разгрузить сервера и кашать реално 500.000.000 комментариев в памяти.
      это очень нужная вешь — она работает в разы быстрее чем мемкаш out-of-process


      1. PlanetsDrawing
        01.05.2015 08:53
        +2

        Может, ваш выбор — C++? :-)
        PS. Черт, не увидел коммент Andrey2008 строкой ниже :(


  1. Andrey2008
    30.04.2015 22:20
    +15

    Философски. А ведь можно было просто использовать Си++. Собрать 64-битное приложение и вперёд. :)
    Это видимо тот интересный переходный момент, когда преимущества языка превращаются в ограничения, которые мучительно преодолеваются.


    1. jonie
      30.04.2015 22:32
      +1

      да можно было просто хранить объекты .net в unmanaged памяти и спокойненько к ним обращаться и они не были бы подхвачены GС — было бы желание…


      1. itadapter Автор
        30.04.2015 22:54
        -1

        I think you are missing the point. The point is to use EXISTING objects that exist for absolutely different purposes, i.e. business logic, not DTO.
        Why would I create 1000s of DTO objects in an unmanaged heap just to cache data. I already have 100s of business types with complex logic,
        some with visitor pattern and double-dispatch etc… (i.e. friend-friend) interaction.
        Pile allows me to use polymorphism and full features of C#, while not loading GC at all


        1. jonie
          01.05.2015 00:37
          +6

          Да какая разница что хранить? Хранятся только данные, методы никак к данным не относятся (не зря же туда this передают?)). Структура .net объектов в памяти ниразу не секретная — переместить данные в неуправляемую кучу и поднастроить ссылки дело нехитрое, более того это уже даже на конференциях показывают — например как шарить .net объекты (как объекты) между процессами в shared memory…


          1. Master_Dante
            01.05.2015 14:35
            -1

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


            1. kekekeks
              01.05.2015 15:54
              +2

              На CLRium-ах было несколько раз. Единственное, код становится ни разу не переносимым, на Mono уже не запустить.


            1. sidristij
              01.05.2015 19:38
              +3

              Скромно оставлю ссылку =) clrium.ru


          1. itadapter Автор
            01.05.2015 19:52
            -1

            Pile — это 100% managed solution, именно поэтому он переносим легко межды платформами и применим к любым бизнес типам.
            То что предлагаете вы — абсолютно не портабельно и делает невозможным работу с objects в стандартной парадигме работы в C#.
            How do you access a «property» of a byte buffer via a pointer? Of course its all the same processor opcodes at the end of the day.
            It would be easier to use C++ and forget about C# in your case.


            1. kekekeks
              02.05.2015 13:40

              How do you access a «property» of a byte buffer via a pointer? Of course its all the same processor opcodes at the end of the day.
              В том и суть, что весь остальной код думает, что это обычный объект и работает с ним по обычной же ссылке на объект.


    1. kekekeks
      30.04.2015 22:33

      Просто автор не знал про наличие в языке value-типов, специально для таких штук предназначенных.


      1. itadapter Автор
        30.04.2015 22:39

        а как хранить ссылки в inside value type? на что ссылатся? на другои валуе type? это как, ану расскажи?


        1. kekekeks
          30.04.2015 22:46

          Делаете структурку типа

          struct Link<T>
          {
               int PoolId; //id массива
               int ObjectId; //по факту индекс в массиве
               //Тут всякая всячина с реализацией Equals, == и т. п.
          }
          
          её храните в своих «объектах» вместо настоящей ссылки и потом резолвите. К ней, соответственно, extension-метод
          static T Resolve<T>(this Link<T> link)
          {
          //тут обрабатываете логику пулов
          }
          


          В самом «объекте» храните ещё поле Self, чтобы на него можно было без геммороя ссылаться.

          Итого: из оверхеда дополнительно тратим 8 байт памяти на объект для Self, ссылки как были 8 байт, так и остались.


          1. itadapter Автор
            30.04.2015 22:50

            The Pile concept allows you to work with CLR Object of ANY STRUCTURE.То что вы предлагаете — это какои-то edge-case for some particular «object».


            1. kekekeks
              30.04.2015 22:56
              +15

              >>ANY STRUCTURE
              Ложь и провокация. Если я положу туда Stream со ссылкой на unmanaged-ресурс, то оно не будет сериализовано. И не edge-case, а просто другое представление структуры объекта.

              И это, вы можете на каком-то одном языке писать? Раздражает.


              1. itadapter Автор
                30.04.2015 23:08
                +3

                Sorry, don' have ru-keyboard. my typing sucks.

                You are absolutely correct. One can not put a stream with unmanaged handles in it.
                But, like I have said, the concept is used for PRACTICAL cases which are as follows:
                in tasks where one needs to store very many graph nodes resident in memory, AND retain the business purpose of every node,
                instead of creation of DTOs that are only purposed for marshalling/storage, the Pile allows you to store «data with code».
                It is a practical system, not a puristic one. So you are correct. you can't store delegates, unmanaged handles etc… but I have never needed it

                You store in pile what you would have stored in Redis(ore memcache). Only faster. Why? because the data is already here, and serialization is orders of magnitude faster that JSON->string->redis.

                Pile: [«any» object] -> bin ser -> byte[] pile(already alloced)
                Memcache/Redis: [«any object»] -> JSON ser/bin ser->network/TCP-> memcache/redis server

                One would think that serialization is very slow, I thought this when we started the project. The reality is different,
                — we have not even expected to achieve such a good PRACTICAL result.

                I hope this makes more sense now.


                1. kekekeks
                  30.04.2015 23:18
                  +2

                  Тут идея в том, что в случае хранения всего в value-типах накладываются примерно те же самые ограничения (можно хранить только POCO), но за счёт дружественной к GC структуры данных можно избежать расходов на сериализацию/десериализацию, ненужного GC pressure (value-типы размещаются на стеке, а не в куче) и т. д.
                  При этом сами данные могут быть любые. За счёт использования generic-типов можно построить инфраструктуру, которая будет обрабатывать любой тип соответствующий требованиям.
                  То есть, получаем те же самые преимущества, но ещё эффективнее.

                  Я бы соорудил proof-of-concept, но для нормального сравнения производительности надо писать свой аналог malloc, а это слишком лениво делать перед праздниками.

                  А так идея довольно стара и активно мной использовалась ещё в школьные времена для программирования на QuickBasic, где не было объектов и ссылок, только структуры и массивы


                  1. itadapter Автор
                    01.05.2015 00:15
                    +3

                    Pile does not have to be local-only. It may be distributed. Also, there is Cache interface that adds key index on top of Pile. The index may be distributed as well. PilePointer has NodeID field. So it is not local-only solution, it may be a hybrid solution, where the most frequent data is kept in ram, some on the nearby server etc.

                    I encourage you to take a look at these:
                    github.com/aumcode/nfx/blob/master/Source/NFX/ApplicationModel/Pile/IPile.cs
                    github.com/aumcode/nfx/blob/master/Source/NFX/ApplicationModel/Pile/ICache.cs


                    1. kekekeks
                      01.05.2015 13:56

                      Вам надо было брать Akka с шардингом и event sourcing, вот что.


          1. leventov
            02.05.2015 16:15

            Ага, а потом когда выясняется что эти структуры надо уметь удалять, начинаются пляски с аллокациями, списками свободных индексов, отдельные треды для компактификации… и элегантные шорты превращаются в Neo4j! В лучшем случае. А в худшем — в ту же самую управляемую кучу, только свелосипеженную с нуля. Ой…


            1. kekekeks
              02.05.2015 16:16

              Ну так предложенный автором Pile этих проблем не отменяет.


              1. leventov
                02.05.2015 16:22

                Да, не отменяет. Это, так, мысли в слух. Просто прикольно понимать, что managed языки, unmanaged языки и базы данных — это, на самом деле, отдельные области единого спектра.


                1. kekekeks
                  02.05.2015 16:25

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


                  1. itadapter Автор
                    02.05.2015 16:50

                    Пиле — 100% полный memory manager, с delete(ptr).
                    Вы можeте отпускать кусочки любои длинны.
                    Смотрим в код:

                    github.com/aumcode/nfx/blob/master/Source/NFX/ApplicationModel/Pile/IPile.cs#L83-129


                  1. itadapter Автор
                    02.05.2015 16:53

                    Кеш — следуюший уровень абстракции — он умеет «забывать» objects после Х секунд, или в какой-то рубеж.

                    Смотрим АПИ:
                    github.com/aumcode/nfx/blob/master/Source/NFX/ApplicationModel/Pile/ICache.cs


          1. semens
            02.05.2015 17:37
            +1

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



        1. itadapter Автор
          01.05.2015 02:14

          Если у вас даже сплошные value types все равно надо хранить строки. А строка, как ни крути — референс тип. Нy не быду же я свои строки делать?


          1. kekekeks
            01.05.2015 13:58

            Нy не быду же я свои строки делать?
            А в чём проблема? Делаете под них точно такой же пул, только состоящий из большого массива char-ов. И храните оффсет в нём и длинну.


            1. itadapter Автор
              01.05.2015 15:52
              -1

              именно это и делает SlimSerializer, на котором стоит LocalPile, под капотом. Просто от вас ето скрыто и не нужно об этом думать когда пишеш бизнес логику


              1. kekekeks
                01.05.2015 15:53

                Скрытость под капотом даёт накладные расходы на сериализацию и GC-pressure. Вы с GC вроде как боролись, нет?


                1. itadapter Автор
                  01.05.2015 19:58
                  -1

                  зачем вы тогда исползуете C#? C++ намного больше подходит для етих целей. статья была вообше не об этом.
                  мои GC расходы — это Gen0 а не Gen2 — вот и вся разница


                  1. kekekeks
                    02.05.2015 13:43
                    +1

                    зачем вы тогда исползуете C#?
                    Потому что он всеяден. Когда надо — можно аккуратно сделать такое вот эффективное с точки зрения GC решение. Ещё структуры очень активно используются в геймдеве (XNA, Unity3D), когда вообще нельзя создавать GC pressure.


                    1. itadapter Автор
                      02.05.2015 16:47
                      -1

                      я же много раз уже писал, что Pile предназначен для ПРЕДМЕТНОЙ области.
                      Если у вас есть уже десятки классов которые моделируют задачу. О каких структурах идет реч? В БОЛТ.дб уже есть около 200 таблиц в базе,
                      я их беру и храню в кеше который сидит на пиле — Это скрыто от меня, в этом весь бенефит.

                      Вот весь код:
                      что храним: github.com/aumcode/nfx/blob/master/Source/NFX/DataAccess/Distributed/Parcel.cs
                      как храним: github.com/aumcode/nfx/blob/master/Source/NFX/ApplicationModel/Pile/ICache.cs



    1. itadapter Автор
      30.04.2015 22:37
      +3

      blog.aumcode.com/2013/08/what-is-nfx-what-is-unistack.html
      NFX это УНИСТАК — pile is less than 1% его features, невозможно просто взять и хранить + обрабатывать чтото в C++ — там совсем другая модель всего — значит опят надо все переписивать тепер на C++: logging, cluster process control, configuration, UDDI, networking stack.
      В етом и весь смысл — не переписиват все осталное 25 раз. Обычно в таких случаях исползют мемкаш или редис — но ето намного менее еффективно чем держaть все прамо в рам. Например етот подход исползуется нами в граф-дб для нахождения друзеи.


      1. Dywar
        30.04.2015 22:56
        -4

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


        1. antonpv
          01.05.2015 14:42
          +4

          Вы бы сами делали поменьше ошибок, а не другим советовали.


          1. Dywar
            01.05.2015 21:55

            Точно :)


        1. EngineerSpock
          01.05.2015 19:24
          -1

          «Не приятно» должно писаться слитно в вашем предложении.


      1. EngineerSpock
        01.05.2015 20:50

        Вы ранее упоминали, что у вас нет периодически клавы с русской раскладкой. А в данном случае она у вас то появлялась, то исчезала, что-ли? Не то, чтобы придираюсь, просто любопытно.


        1. itadapter Автор
          01.05.2015 21:00
          -3

          не печатал по-русски где-то 23 года… заржавел
          не пойму однако, почему это так важно для многих


          1. EngineerSpock
            01.05.2015 23:32

            Может быть, примерно потому что, если я влезу на Stackoverflow и начну писать на немецком, английском и французском одновременно, периодически разбавляя русским, то это будет выглядеть несколько странным? :) Понимаю, что аналогия несколько отличается, но частично совпадает :)
            А вообще, с почином вас на Хабре! Пишите ещё. Хардкорные технические статьи всегда на пользу. Но, действительно, пожалуйста, если можно — поменьше сленга и выпендрёжа местами. Возможно, это потому что по-русски давно не писали. В общем, немного поскромнее, поподробнее и было бы вообще замечательно :)


  1. zenden2k
    30.04.2015 23:05
    -10

    Легче было переписать на языке без сборщика мусора


    1. leventov
      30.04.2015 23:36
      +11

      Не легче.


  1. int19h
    01.05.2015 05:15
    +3

    Кстати, очень похожим образом работал класс XPathDocument. Там после парсинга все XDM-дерево хранится именно вот так, отображением на большие массивы байт индексацией. И обход дерева через XPathIterator ходит по этим байтам (в которых хранятся и относительные сдвиги на соседние узлы по разным осям).


  1. zed91
    02.05.2015 07:04
    -1

    Сам пишу в основном на .NET. Но тут явно напрашивается С++. CPU Профайлер + _CRTDBG_MAP_ALLOC превращают кривонаписанные поделки в high-load ready приложения… И никаких больше «danGling» указателей


    1. GrigoryPerepechko
      02.05.2015 15:07
      +4

      Вы лихо за 10 минут переписываете гигантские проекты которые доросли до hiload на плюсы?


      1. zed91
        02.05.2015 17:38
        -1

        Я предпочитаю потратить больше времени на разработку архитектуры, чем на кодописание и фишечки. Если проект вырос в high load из наколенной поделки, значит изначально не было понимания требований, переписывание тут ни при чем.


        1. itadapter Автор
          02.05.2015 18:24

          вы бы глянули сначала в код:

          github.com/aumcode/nfx/blob/master/README.md

          особенно интересно:
          github.com/aumcode/nfx/blob/master/Source/NFX/DataAccess/Distributed/StoreIntfs.cs

          потом оценили объём решаемои задачи, а потом писали про поделки