Обновить

Как я научил свою читалку различать примечания и комментарии в «Войне и мире»

Я не профессиональный программист. Пишу Android-читалку MRead для себя и для тех, кому тоже надоели комбайны. Код закрытый, всё работает локально, без серверов и трекинга. Раздаю через RuStore, 4PDA и GitHub.

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

Проблема

Открываю «Войну и мир». В одном предложении сразу два маркера сноски: примечание (перевод французского) и научный комментарий. В разных изданиях они нумеруются независимо, поэтому в тексте легко встретить два маркера с одинаковой цифрой. Старая логика по тапу вытаскивала из текста ближайшую цифру и искала абзац, начинающийся с этого числа, по всем главам, и показывала первое совпадение. Итог: тап по «6» открывал не ту сноску, а тап по числу вроде «1805» вообще пытался найти несуществующую сноску.

Почему наивный подход не работает

Цифра это не идентификатор. Она повторяется и в каждой главе, и между разделами «Примечания» и «Комментарии». А вот в исходнике всё однозначно: каждый маркер это ссылка с уникальным адресом.

EPUB: 1 FB2: [6] и {4}

То есть книга всегда знает правильный ответ. Беда была в том, что мой парсер при импорте выбрасывал href и оставлял только видимый текст маркера.

Что я поменял

  1. При разборе HTML сохраняю не только текст абзаца, но и ссылки-сноски с точной позицией маркера. Позицию считаю надёжно: оборачиваю маркер невидимыми символами из Private Use Area до очистки текста, а после очистки нахожу их и вычисляю смещение. Так цифры в обычном тексте (годы, числа) не путаются с маркерами.

  2. Позиции храню в абсолютных координатах главы. Это важно, потому что мой пагинатор режет абзацы на куски по строкам. Абсолютные смещения переживают нарезку без отдельной возни в пагинаторе.

  3. По тапу беру не цифру, а ссылку под пальцем, и резолвлю сноску по точному id из нужного файла. Для FB2 пришлось ещё и сохранить id секций сносок при конвертации в HTML, потому что примечания и комментарии лежат в отдельных главах.

  4. Старый поиск по цифре оставил как запасной вариант для книг без нормальной разметки, но убрал срабатывание на голые числа.

Результат: тап по примечанию открывает перевод, тап по комментарию открывает комментарий, а год «1805» больше никого не трогает.

Контекст для тех, кому интересно

Рендер текста у меня не WebView, а нативный движок на Canvas с собственной пагинацией (Jetpack Compose, Kotlin). Это даёт контроль над переносами, выравниванием по ширине и стабильной привязкой цитат и закладок, но за каждую такую фичу приходится платить ручной работой вроде этой истории со сносками.

Что ещё в 1.4.0, кратко

• Полнотекстовый поиск по PDF (для PDF с текстовым слоем)
• Озвучивание (TTS): голоса, скорость, таймер сна, автопереход, пауза с памятью места
• Передача слова во внешний словарь и контекст предложения в экспорт Anki
• Настраиваемые блоки настроек чтения, межабзацный интервал, Bold/Italic
• Полки, фоновый импорт из папок, оценки и заметки, Material You

Ссылки

• RuStore

• GitHub

• 4PDA

Если у вас была книга FB2, переоткройте её, чтобы заработали точные сноски (HTML генерится при импорте). EPUB подхватит сам.

Буду рад замечаниям по подходу. Если делали резолв сносок иначе, расскажите как, мне правда интересно.

Теги:
Всего голосов 3: ↑3 и ↓0+6
Комментарии0

Как мы запустили Qwen3.8–27B целиком на RTX 5060 8 GB и получили ~30 токенов/с

Наш проект называется ExVRAM Lab. Это открытая исследовательская лаборатория, в которой мы проверяем, насколько большие локальные LLM можно запускать на обычных видеокартах с ограниченным объёмом VRAM, если использовать ultra‑low‑bit quantization, полное размещение весов на GPU и существующие open‑source inference‑технологии.

ExVRAM расшифровывается как Exchange Compute for VRAM. Основная идея проекта — в ряде сценариев выгоднее потратить часть свободной вычислительной мощности GPU на работу с более компактным представлением весов, чем хранить часть модели в оперативной памяти и постоянно передавать данные через PCIe.

Когда мы начинали проект, исходный вопрос был достаточно простой: можно ли запустить dense‑модель примерно на 27 миллиардов параметров на видеокарте всего с 8 ГБ VRAM так, чтобы она не просто «запустилась», а работала полностью на GPU, поддерживала длинный контекст и обеспечивала нормальную интерактивную скорость генерации.

В качестве основной тестовой системы мы используем NVIDIA GeForce RTX 5060 8 GB на архитектуре Blackwell. Основная модель в текущих экспериментах — Qwen3.8–27B.

Сначала результат выглядел не слишком впечатляюще. Модель запускалась, но значительная часть весов оставалась в системной памяти. Около 5,7 GiB весов находилось на GPU, ещё примерно 2,5 GiB — на CPU. Скорость генерации составляла порядка 3,8–5,5 токена в секунду.

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

Это стало одним из первых важных наблюдений проекта. Проблема заключалась не столько в нехватке вычислительной мощности RTX 5060, сколько в том, что часть decoder weights находилась в RAM. Во время autoregressive generation данные приходилось постоянно передавать между CPU и GPU через PCIe.

Как мы запустили Qwen3.8–27B целиком на RTX 5060 8 GB и получили ~30 токенов/с

Публикации