| Скорости: от стека до кучи (10.11.2026). |
|
| 2026 - Ноябрь | |||
| 10.11.2026 17:42 | |||
Гугл начал говорить, что в Borland C++ Builder куча медленнее стека в сотни раз: из-за операций выделения и освобождения памяти - но при этом чтение из неё равно скорости стека. Но стоит переформулировать запрос - и он пишет, что чтение из кучи медленнее стека во много раз из-за хаотичных прыжков по памяти до нужного значения. Переформулировал в 3-й раз - куча медленнее в чтении на 10-30%. Стало опять понятно, что вера - слепа, и уж точно её к гуглу применять нельзя (особенно после последнего разговора). Итак, char cTemp[1] в 10-кратном цикле 4млдр вызовов, с вычетом времени цикла вызова, - отрицательное время на потоке частотой 3.57ГГц в неидеальных условиях (виртуалка, дочерняя ОС мешает + даже такого алгоритма не хватит на такую мелочь). Выносим в родительскую ОС на отдельный поток - опять отрицательное время выполнения. Не гордый - использовал гугла как раба и сгенерировал 100к записей по 40 в строке (компилируется - моё почтение: минуты 2-3). Перенёс вызов из Form_Activate в Form_Create (чтобы всплывающее окно и значок в трее не ели ресурсы). 224066-231286ГБ/с - корректный результат, с учётом убирания всех тормозящих факторов. Далее приходит осознание собственной ошибки. Для char *cTemp = new char[1] (1 4-байтный указатель) более подходит запись char *cTemp[1] (массив с 1 4-байтным указателем), чем char cTemp[1] (массив из 1 1-байтного символа). Ну и кто мне припишет звёздочки к ста тысячам cTemp? Правильно: не гугл, а автозамена. И [1] она же сотрёт, чтобы был 1 4-байтный указатель, - полный аналог new. 893400-897210ГБ/с - выделение указателя всегда быстрее (есть к чему стремиться, в контексте ускорения генератора выше). Память в сегменте данных, между стеком и кучей, - выделяется при запуске программы - стек, даже при своей бешеной скорости, уже проиграл. Настал черёд кучи через new. Внутренний цикл понижается до 200млн и внешний цикл до 1 (2ГБ памяти - предел для 32-битного Borland C++ Builder v.6.0). Abnormal program termination. Потому что в 64-битной родительской ОС указатель 8Б - значит, указанные выше скорости надо умножить на 2 ещё: учитывалась 1 итерация как 4Б. Ну, чтобы не убивать результаты, new тоже учитывается как 4Б - "0.0733-0.0737ГБ/с". Усредняя: инициализация в стеке - в 12.18млн раз быстрее, чем в куче. И это ещё без учёта, что после new не только delete надо делать, но ещё и NULL (о проблеме диких указателей - абзацем ниже). Теперь нужно в циклах присвоить индекс цикла unsigned int (защита от кеширования) в заранее инициализированный указатель char*, считая его как 1Б с постоянным переполнением (может быть замедление за счёт runtime-проверок - поэтому добавляем unsigned). Программа падает из-за дикости указателя unsigned char *cTemp. Изменение конструкции: unsigned char cValue; unsigned char *cTemp = &cValue;. Лень мерить среднее - внешний цикл 100 при внутреннем 4млрд - 8.152ГБ/с. Теперь переменные делаются глобальными, чтобы их унесло в сегмент данных, - даже static не подписал, 26.596ГБ/с. А сегмент данных-то побыстрее стека будет, раза в 3. И объём проверен 26МБ в своё время (при 64МБ - падает) - против 1МБ стека (увеличиваемый до 10МБ в настройках проекта). А в современных языках - он где 2ГБ, а где вообще не ограничен ничем. А теперь задайте вопрос ИИ гугла "какого х ты пздл всё это время?!" - и он найдёт аж 3 причины этого феномена: время на выделение памяти в стеке, кеширование ЦП, прямая индексация против относительной. А что new? У кучи есть в этом тесте большое преимущество: не надо прыгать между адресами RAM, чтобы собрать нужные данные. И оказывается между стеком и сегментом данных - 12.736ГБ/с. Не, так дело не пойдёт: надо заставить программу метаться между адресами в RAM. Удалось выделить только 600МБ (External Exception EEFFACE) - 1.576ГБ/с. В почти 6 раз медленнее стека - при этом, хаос скорости при одной и той же операции (пляшет 1.1-1.99ГБ/с). Теперь new надо сделать из сегмента данных, чтобы убедиться, что объект переместился в кучу. А вот ни хрена: new остался в сегменте данных - и шпарит 13ГБ/с. Какое-то недосегмент-недокуча получилось. Обратно перенёс в функцию, чтоб железно в кучу, - единицы ГБ/с. Это единственный непонятный эксперимент из прошедших. (добавлено 11.11.2026) Протестировал граничный размер сегмента данных для Borland C++ Builder v.6.0. 52МБ (не МиБ). Между 52 и 53 мегабайтом - возникает неопределённость стабильности. Может заработать. Может ошибку линковки показать, убрать её и заработать. Может свалиться. |
|||
| Обновлено ( 11.11.2026 12:23 ) |