На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
  
> Ограничение по размерам данных
    Всем привет!

    Сразу хочу сказать - мой вопрос не практический, а чисто академический. Заранее прошу прощения за некоторую гиперболизацию в своих высказываниях, бикоз настроение хорошее. Понедельник, честно сказать, гуано полное. Но я всё порешал на высшем уровне. Я - кросава, и теперь с самому вопросу... Под такое настроение я решил выяснить, а могу ли я в качестве поля класса использовать массив размером в один зетабайт? Как говорится "чего уж мелочиться". Ну да, я тут немного перегнул. Поговорил с ChatGPT4, он мне выдал такую инфу:

    Цитата
    В 64-разрядной системе теоретически максимальный объем адресуемой памяти составляет 16 эксабайт (16 ЭБ), что эквивалентно 264 байт. Однако на практике существуют ограничения, которые могут уменьшить этот объем:

    • Физические ограничения: Хотя архитектура 64 бита позволяет адресовать до 16 ЭБ, многие современные процессоры и операционные системы имеют ограничения на фактический объем поддерживаемой памяти. Например, большинство процессоров x86-64 используют 48-битные адреса, что ограничивает адресуемую память до 256 терабайт (ТБ).
    • Ограничения операционных систем: Разные операционные системы имеют свои собственные ограничения на максимальный объем поддерживаемой памяти. Например, Windows Server может поддерживать до 24 ТБ, а некоторые версии Linux могут поддерживать до 64 ТБ.

    Но вот на второй мой вопрос к ChatGPT4, а-ля, "А в Стандартах С/С++ есть какие либо ограничения или указания по максимально-допустимым размерам объектов?" я ответ получил, типа "такого нет".

    Собственно вопросов несколько

    1) "Граничные условия" - вестчь важная, она должна быть как-то регламентирована? Если да - то где и как?
    2) А если "не", то как "правильно быть"?
      Прочитал на cplusplsu.com про массивы и там ничего не сказано про тип индексов для элементов. Тогда нет ограничения для размеров массивов (хоть вся доступная память). Главное, чтобы ОС смогла для этого объекта найти достаточно свободных страниц памяти и они все разом влезли в RAM (т.к. объект будет загружаться полностью перед доступом).

      И еще одна догадка. Как тогда работают сейчас нейросети (тот же ChatGPT). Это же по сути массивы на Гб, если не на Тб значений.
      Сообщение отредактировано: macomics -
        macomics, вся фишка в том, что всё в С/C++ должно и обязано соответствовать Стандартам. Это аксиома. Любой шаг вправо или влево - это ересь. Вот поэтому я и спрашиваю именно за стандарты в первую очередь.

        Цитата macomics @
        И еще одна догадка. Как тогда работают сейчас нейросети (тот же ChatGPT). Это же по сути массивы на Гб, если не на Тб значений.

        Прочитай про mmap. Навскидку не скажу, но на 100% помню, что в Уиндофс есть схожий по функционалу механизм.
          Цитата Majestio @
          Прочитай про mmap. Навскидку не скажу, но на 100% помню, что в Уиндофс есть схожий по функционалу механизм.

          Это просто WinAPI CreateFileMapping. Он же есть и в Linux
          Прикреплённая картинка
          Прикреплённая картинка


          Добавлено
          Про 32-битный режим я могу сказать точно. На С можно было объявить массив размером в 4Гб (максимальный доступный объем адресов). А вот для 64-битных надо искать, но по идее ограничения тоже нету.
          Сообщение отредактировано: macomics -
            Ну да, я наверное про это и говорил. Просто в WinAPI я мало-мало путаюсь. Судя по докам, если вызов неуспешный - возвращается NULL. Законный вопрос - а как узнать сколько я могу резервировать памяти так, чтобы эта функция отработала правильно?
              https://learn.microsoft.com/ru-ru/windows/w...ocessmemoryinfo

              еще это

              https://learn.microsoft.com/en-us/windows/w...balmemorystatus

              Добавлено
              Кстати замечу. В 32-битном режиме физическая адресация (с PAE) возможна до 64 Гб (36 бит), но адресное пространство физически ограничено 32-битами (4 Гб) и как раз этим и был ограничен максимально возможный размер массива (4 Гб).
              Сообщение отредактировано: macomics -
                https://en.cppreference.com/w/c/types/size_t
                Цитата
                size_t can store the maximum size of a theoretically possible object of any type (including array).

                size_t is commonly used for array indexing and loop counting. Programs that use other types, such as unsigned int, for array indexing may fail on, e.g. 64-bit systems when the index exceeds UINT_MAX or if it relies on 32-bit modular arithmetic.


                Соответственно, объект не может быть больше максимального значения size_t
                  Любой контейнер имеет метод max_sixe(), но по Стандарту он ограничивает потенциал контейнера, т.е. без учёта фактических ограничений исполнительной подсистемы. И вообще-то это логично, т.к. последний зависит от многих факторов, выходящих за рамки Стандарта. Фактический же не имеет чёткой границы. Вот надо тебе только то, что умещается в памяти или же ты согласен на своп. Если согласен, то до какой степени. На HDD у тебя будут одни предпочтения, на SDD ты можешь себе позволить больше свопа, и ещё больше, если не SATA, а NVMe. Итп.

                  Добавлено
                  Я как-то использовал такой код на WinAPI. Сорри, без комментов. Пользуйся, если надо.
                  ExpandedWrap disabled
                    template <typename T>
                    size_t getOptimizedSize()
                    {
                      if constexpr (sizeof(void*) > 4)
                      {
                        size_t size    = std::numeric_limits<decltype(size)>::max(),
                               oldSize = size;
                        void  *mem;
                      
                        do
                        {
                          mem = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
                          if (mem == NULL)
                          {
                            oldSize = size;
                            size  >>= 1;
                            continue;
                          }
                      
                          size_t newSize = (oldSize >> 1) + (size >> 1);
                      
                          oldSize = size;
                          size    = newSize;
                          VirtualFree(mem, 0, MEM_RELEASE);
                        } while (size != oldSize);
                      
                        size_t allocated = size;
                      
                        mem = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
                        if (mem == NULL) throw std::runtime_error("Ops!");
                        std::cout << "Allocated " << size << " bytes" << std::endl;
                      
                        do
                        {
                          if (SetProcessWorkingSetSize(GetCurrentProcess(), size, size) == FALSE)
                          {
                            oldSize = size;
                            size  >>= 1;
                            continue;
                          }
                      
                          size_t newSize = (oldSize >> 1) + (size >> 1);
                      
                          oldSize = size;
                          size    = newSize;
                        } while (size != oldSize);
                      
                        if (SetProcessWorkingSetSize(GetCurrentProcess(), size, size) == FALSE)
                          throw std::runtime_error("Ops 2!");
                        std::cout << "Residented " << size << " bytes" << std::endl;
                      
                        do
                        {
                          if (VirtualLock(mem, size) == FALSE)
                          {
                            oldSize = size;
                            size  >>= 1;
                            continue;
                          }
                      
                          size_t newSize = (oldSize >> 1) + (size >> 1);
                      
                          oldSize = size;
                          size    = newSize;
                          VirtualUnlock(mem, oldSize);
                        } while (size != oldSize);
                      
                        if (VirtualLock(mem, size) == FALSE)
                          throw std::runtime_error("Ops 3!");
                        std::cout << "Locked " << size << " bytes" << std::endl;
                      
                        SetProcessWorkingSetSize(GetCurrentProcess(), size, size);
                        VirtualFree((char*)mem + size, allocated - size, MEM_DECOMMIT);
                      
                        std::cout << "Ok" << std::endl;       // <- тут бряк
                     
                        VirtualUnlock(mem, oldSize);
                        SetProcessWorkingSetSize(GetCurrentProcess(), -1, -1);
                        VirtualFree(mem, 0, MEM_RELEASE);
                     
                        return size / sizeof(T);
                      }
                      return std::numeric_limits<size_t>::max() / sizeof(T);
                    }
                  Костыли местами, но в нынешних реалиях приемлемые. Тут предполагается, что в 32-битном окружении памяти достаточно для максимального количества элементов, а для 64-битных подсчитывается, исходя их реалий на текущий момент, предполагая, чтобы свопов не было. Сначала пробуем max, который наверняка не получится, поэтому постепенно, до успеха, уменьшается вдвое. Затем дихотомией увеличивается между успешным и предшествующием ему неуспешным. Эта память будет включать своп. Затем пробуем поднять предел рабочего пространства процесса до предела, ограниченного, например, администратором. Ну или там контроллером домена, например, неважно. Алгоритм аналогичный. Обычно он гораздо меньше только что вычисленного до этого, и в теории не должен свопиться, но только как рекомендация. На практике, если вдруг ОСи внезапно что-то сфорсмажорит, свопнуться сможет. Наконец пробуем залочить регион, отключая т.с. своп полностью. И тем же алгоритмом. В итоге размер памяти будет если не максимально возможным, то близко к этому, т.к. в процессе лока ОСь будет пытаться выгрузить из памяти всё ненужное, за исключением чего-то там на её усмотрение для обеспечения тех же самых форсмажоров.

                  Добавлено
                  Запуск на домашней тачке (32Г RAM + NVMe) выдал:
                  ExpandedWrap disabled
                    Allocated 91625968979 bytes
                    Residented 22906492243 bytes
                    Locked 15270994816 bytes
                    Ok
                  Всего максимум памяти 90Гб, из них размер рабочего пространства почти 23, и наконец гарантировано несвопируемого 15. До запуска память была забита на 68%, после запуска на 30%. Процесс лока занял порядка 20 секунд. Причём после Ok я там добавил ожидание перед освобождением и выходом, и во время этой паузы система оставалась более чем отзывчивой. Win10Pro.

                  Добавлено
                  P.S. Прикольно, но если во время пауз позапускать ещё копии, то вот результаты последовательных запусков, пока предыдущие всё ещё удерживают свои залоченные регионы:
                  ExpandedWrap disabled
                    Allocated 68719476733 bytes
                    Residented 6442450941 bytes
                    Locked 4294967291 bytes
                  ExpandedWrap disabled
                    Allocated 68719476733 bytes
                    Residented 1610612733 bytes
                    Locked 1073741819 bytes
                  ExpandedWrap disabled
                    Allocated 68719476733 bytes
                    Residented 536870909 bytes
                    Locked 357913937 bytes
                  Ну итп. Т.е. ОСь всё ещё оставляет память на форсмажоры и всё ещё более чем отзывчива. После 10 запусков физическая память занята на 91%, запас прочный.

                  Добавлено
                  Не думаю, что Стандарт тут чем бы там ни было мог бы помочь. Это уже не его сфера ответственности. После всех выходов память занята на 24%.
                  Сообщение отредактировано: Qraizer -
                    Всем спасибо. Qraizer, отдельный респект за тесты и пример. Я так понимаю, что Стандарт этим не занимается. Но и в этом вижу профит - не указывает, но и не ограничивает. Значит будем посмотреть в компиляторах.
                    0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                    0 пользователей:


                    Рейтинг@Mail.ru
                    [ Script execution time: 0,0444 ]   [ 18 queries used ]   [ Generated: 1.12.24, 19:52 GMT ]