Bug 60883
| Summary: | i586-glibc-core (p11): `memcpy`/`memmove`/`memset` без IFUNC (старый i586-код, читает приёмник): 32-битные игры в 6–9 раз медленнее, чем во Fedora/Flatpak | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| Product: | Branch p11 | Reporter: | |||||||
| Component: | glibc-core | Assignee: | qa-team <qa-team> | ||||||
| Status: | UNCONFIRMED --- | QA Contact: | qa-p11 <qa-p11> | ||||||
| Severity: | normal | ||||||||
| Priority: | P5 | CC: | antohami, mail, placeholder, zerg | ||||||
| Version: | unspecified | ||||||||
| Hardware: | x86_64 | ||||||||
| OS: | Linux | ||||||||
| See Also: | https://bugzilla.altlinux.org/show_bug.cgi?id=60875 | ||||||||
| Attachments: |
|
||||||||
Created attachment 22376 [details]
Патч alt/glibc.spec (p11): включить multiarch (IFUNC) для i586, сборка как i686
## Причина найдена в spec, патч проверен сборкой и на реальной игре
Прикладываю патч `glibc-2.38.0.223.f053ff-alt-i586-multiarch.patch` (ветка p11, файл `alt/glibc.spec`; накладывается через -p1).
### Где причина
В `alt/glibc.spec` (и в p11, и в sisyphus):
```
%ifarch x86_64
%def_enable multiarch # configure получает --enable-multi-arch
%else
%def_disable multiarch # для %ix86 multiarch выключен
%endif
```
Для i586 multiarch выключен, поэтому в `i586-glibc-core` нет IFUNC (memcpy/memmove/memset/strlen/strcmp/memcmp и др. — обычные функции i586,
в частности `memcpy` читает приёмник перед записью). В 32-битной glibc Flatpak runtime 26.08 таких IFUNC-символов 48.
(В 2011 году multiarch включали и на %ix86 — 6:2.11.3-alt3, позже условие сузили.)
### Почему недостаточно просто включить multiarch для %ix86
Пробная сборка с `%ifarch x86_64 %ix86` падает на линковке:
`undefined reference to '__memcmp_ia32'`.
Оптимизированные i386-варианты лежат в `sysdeps/i386/i686/multiarch` и подключаются только при конфигурации под i686.
### Что делает патч
1. Включает `multiarch` для `%ix86`.
2. Для `%ix86` добавляет `--build=i686-alt-linux --host=i686-alt-linux` в `%configure`.
### Проверка
- Сборка `glibc-2.38.0.223.f053ff-alt1.p11.1` в hasher (`--target=i586`, без `make check`) проходит.
- В полученной `libc.so.6` **48 IFUNC-символов** (память и строки: memcpy, memmove, memset, memcmp, memchr, strlen, strcmp, strchr, strcpy, strcat, str*case* и др.),
набор совпадает с 32-битным libc из freedesktop Platform.Compat.i386 26.08. До патча — 0.
- Пакеты `i586-glibc-core`, `-nss`, `-pthread`, `i586-libnsl1` (релиз `alt1.p11.1.1`) установлены на рабочую систему x86_64 (ALT p11, ядро 6.18.52, Ryzen 7 5800X, Radeon RX 9070 XT).
`rpm -Uvh --test` проходит, на установленной `/lib/libc.so.6` memcpy/memmove/memset/strlen/strcmp/memcmp — IFUNC.
- **Half-Life 2 (нативный 32-битный Steam), «тяжёлое» место:** было 20–30 FPS, стало стабильно как в Fedora/Flatpak (180 FPS при `fps_max 180`), без каких-либо `LD_PRELOAD`.
Раньше это решалось только подменой memcpy/memmove через `LD_PRELOAD`; профиль (gdb) до исправления: 68–83 % времени потока драйвера в `libc memcpy+0x2c`.
### Важные оговорки (прошу оценить мейнтейнеров)
1. Конфигурация под i686 означает, что 32-битная libc требует процессоров i686 (с cmov). Реальные процессоры класса i586 такой пакет не поддержат.
Если поддержка настоящего i586 нужна, то, возможно, стоит собирать для репозитория `x86_64-i586` отдельный вариант под i686 с multiarch (там 32-битные пакеты
нужны только для запуска игр и Wine на x86_64), а чистый i586 оставить как есть.
2. Тесты glibc (`make check`) я не запускал, только сборку и проверку на игре. Подготовленные пакеты — локальная сборка, не для публикации.
3. Проверялась ветка p11 (2.38.0.223.f053ff-alt1.p11.1); в sisyphus (2.43) условие в spec то же, но сборку я там не проверял.
### Вложение
- `glibc-2.38.0.223.f053ff-alt-i586-multiarch.patch`, sha256 `3d494cd39bd7295b8e505abd2e0cf64c94198dd85f2e61293ff134a36b4e7212`
Может, действительно, уже забить на i586 и перезаключить i686, раз уж собрались делать 32-битный репозиторий лишь придатком к x86_64? > перезаключить
переключить на
(Ответ для Sergey V Turchin на комментарий #2) > Может, действительно, уже забить на i586 и перезаключить i686, раз уж > собрались делать 32-битный репозиторий лишь придатком к x86_64? (Ответ для Sergey V Turchin на комментарий #2) > Может, действительно, уже забить на i586 и перезаключить i686, раз уж > собрались делать 32-битный репозиторий лишь придатком к x86_64? Соглашусь, если 32-битный репозиторий нужен только как придаток к x86_64, то glibc-core для него логично собирать под i686 вместо i586. Приложенный патч делает именно это (i686 + multiarch). Проверено: сборка проходит, в libc.so.6 появляются IFUNC (48 шт.), HL2 в «тяжёлом» месте вместо 20-30 FPS работает как в Fedora (180 FPS). make check для glibc я не запускал. |

Description
mail 2026-10-09 10:09:17 MSKCreated attachment 22370 [details] Исправленный исходник, проблема с FPS исчезает - проверено. ## Описание В 32-битной glibc p11 основные функции работы с памятью (`memcpy`, `memmove`, `memset`, `strlen`, `strcmp`, `memcmp`) реализованы обычным i586-кодом без IFUNC-вариантов (SSE2/AVX). Для сравнения: в 32-битной glibc Flatpak runtime 26.08 (freedesktop-sdk) и в Fedora эти символы — `IFUNC`. `memcpy` из `/lib/libc.so.6` (классическая i586-реализация): ``` memcpy: push %edi; push %esi; mov 0xc(%esp),%edi; mov 0x10(%esp),%esi; mov 0x14(%esp),%ecx mov %edi,%eax; cld; cmp $0x20,%ecx; jbe ... neg %eax; and $3,%eax; sub %eax,%ecx; xchg %eax,%ecx; rep movsb mov %eax,%ecx; sub $0x20,%ecx; js ... loop: mov (%edi),%eax ; чтение приёмника mov 0x1c(%edi),%edx ; чтение приёмника sub $0x20,%ecx ; memcpy+0x2c, здесь профиль «залипает» mov (%esi),%eax; mov 0x4(%esi),%edx; mov %eax,(%edi); ... ; блоки по 32 байта ``` Перед записью каждый 32-байтный блок приёмника читается (приём «предзагрузки» кэш-линий для Pentium). На современных CPU и при копировании в память GPU (write-combining/BAR), используемую 32-битными драйверами Mesa и играми, это приводит к многократному замедлению. Для 64-битной glibc (`glibc-core-2.38.0.223.f053ff-alt1.p11.1.x86_64`, `/lib64/libc.so.6`) проблемы нет: `memcpy`, `memmove`, `memset`, `strlen`, `strcmp`, `memcmp`, `memchr`, `strchr`, `strcpy` в таблице динамических символов — `IFUNC`, как и в Flatpak runtime 26.08. Затронут только i586-пакет. ## Влияние (измерено) Half-Life 2 (нативный 32-битный Steam, OpenGL), Radeon RX 9070 XT, Ryzen 7 5800X, ядро 6.18.52: - ALT p11, нативный Steam: **20–30 FPS** в сценах с большим числом объектов (в «лёгких» местах 180); - Flatpak Steam и контейнер Fedora 44 на том же ПК и ядре: **180 FPS** везде; - профиль потоков (gdb, 600 снимков): поток OpenGL-драйвера `gl0` проводит **68–83 % времени в `libc.so.6` memcpy+0x2c**, код Mesa 2–3 %; в Fedora `memcpy` в профиле отсутствует; - подмена только `memcpy`/`memmove` через `LD_PRELOAD` (код ниже) полностью убирает проблему: FPS как в Fedora, Mesa и ядро не менялись (Mesa 26.1.8-alt1; на 26.2.4-alt2 те же 20–30 FPS без подмены). Проблема затрагивает все 32-битные приложения, активно копирующие большие блоки (Steam-игры на движке Source и др.), а не только HL2. ## Обход (LD_PRELOAD, проверен) ```c #include <stddef.h> void *memcpy(void *restrict d, const void *restrict s, size_t n) { void *r = d; __asm__ volatile("cld; rep movsb" : "+D"(d), "+S"(s), "+c"(n) : : "memory"); return r; } void *memmove(void *d, const void *s, size_t n) { unsigned char *dd = d; const unsigned char *ss = s; if (dd <= ss || dd >= ss + n) return memcpy(d, s, n); while (n >= 4) { n -= 4; *(unsigned int *)(dd + n) = *(const unsigned int *)(ss + n); } while (n) { n--; dd[n] = ss[n]; } return d; } ``` Сборка: `gcc -m32 -O2 -shared -fPIC -fno-builtin -fno-tree-loop-distribute-patterns -fno-stack-protector -o libfastmemcpy.so fastmemcpy.c`; параметры запуска Steam-игры: `LD_PRELOAD=/path/libfastmemcpy.so %command%`. ## Как воспроизвести 1. ALT p11, пакет `i586-glibc-core` 2.38.0.223.f053ff-alt1.p11.1, Steam, любая видеокарта AMD. 2. Проверить символы: у `/lib/libc.so.6` (32-бит) `memcpy` в таблице динамических символов типа `FUNC`, не `IFUNC` (например, через `python3` + разбор `.dynsym` или `readelf -Ws /lib/libc.so.6 | grep ' memcpy@'`). 3. Запустить HL2 в «тяжёлой» сцене, сравнить FPS с `LD_PRELOAD` и без. ## Что ожидается Собирать 32-битную glibc в p11 с multiarch (IFUNC для `memcpy`, `memmove`, `memset`, `strlen`, `strcmp`, `memcmp` и др.), как делают Fedora и freedesktop-sdk (сборка для i686+ с выбором SSE2/AVX во время выполнения), либо хотя бы заменить медленные реализации. Если для i586 это сознательно, то прошу рассмотреть отдельный вариант `i686` с multiarch (для x86_64-i586 репозитория, где 32-битные пакеты нужны именно для запуска игр и Wine). ## Окружение ALT Workstation K 11.4 (p11), ядро 6.18.52, AMD Ryzen 7 5800X, Radeon RX 9070 XT, Mesa 26.1.8-alt1, `i586-glibc-core-2.38.0.223.f053ff-alt1.p11.1`. Готов приложить профили (gdb) и логи; проверялась только ветка p11.