Эволюция: в базу только улучшения лучших + счётчики по всем; выборка 10000

По требованию пользователя «не сохранять всё подряд, а только то, что
нужно для эволюции»: рядовые геномы поколения больше НЕ пишутся строками
в runs — писатель получает лёгкие StatsOnly-инкременты (счётчики,
гистограмма, причины отказов на дашборде остаются честными по ВСЕМ
оценкам), а полная строка пишется только когда лучший геном острова
улучшился (+ полировка). Вставка миллионов строк в HDD-базу с 6 индексами
была узким местом конвейера: GPU считал цикл ~23 мин, дозапись хвоста
очереди шла часами (~150 строк/с), реальная скорость была ~165 оценок/с
вместо ~1300/с. gpu_record_worker стал не нужен — удалён.

Выборка эволюции уменьшена до 10000 на поколение (8 островов x 1250,
было 8x4000=32000) — по просьбе пользователя, чтобы поколения сменялись
быстрее; поколений за цикл теперь 120 (было 60) — при освобождённом
писателе цикл углубляется вдвое дальше за то же время.
This commit is contained in:
jze9
2026-07-12 22:29:17 +05:00
parent c232f1d387
commit 7732577f31
7 changed files with 153 additions and 81 deletions

View File

@@ -8,7 +8,14 @@ from gausse.storage.database import count_runs, fetch_runs, open_connection
DB = ComponentDatabase.load()
def test_run_evolution_writes_all_evaluations_and_returns_summary(tmp_path):
def _stats_total(conn) -> int:
row = conn.execute("SELECT value FROM stats_counters WHERE key='total'").fetchone()
return row[0] if row else 0
def test_run_evolution_counts_all_evaluations_but_stores_only_best_rows(tmp_path):
"""Контракт хранения evolve: агрегаты честно считают ВСЕ оценки,
а полные строки — только улучшения лучшего генома острова."""
db_path = tmp_path / "runs.sqlite3"
bounds = SearchBounds(max_stages=2)
@@ -26,9 +33,16 @@ def test_run_evolution_writes_all_evaluations_and_returns_summary(tmp_path):
assert "best_fitness" in summary
conn = open_connection(db_path)
assert count_runs(conn) == 12
assert _stats_total(conn) == 12 # счётчики дашборда видят все оценки
n_rows = count_runs(conn)
# строк меньше, чем оценок: максимум 1 улучшение на остров на поколение
assert 1 <= n_rows <= 2
rows = fetch_runs(conn)
assert all(r.search_mode == "evolve" for r in rows)
# лучший найденный обязан лежать в базе полной строкой
best_row_fitness = max((r.efficiency if r.feasible else -1.0) for r in rows)
if summary["best_feasible"]:
assert best_row_fitness == summary["best_efficiency"]
conn.close()
@@ -67,7 +81,7 @@ def test_polish_does_not_make_the_best_genome_worse():
def test_run_evolution_gpu_batch_mode(tmp_path):
"""GPU-режим эволюции (батч-оценка поколения; здесь numpy-бэкенд):
пишет прогоны в ту же БД и находит реализуемые конфигурации."""
агрегаты считают все оценки, строки — только улучшения лучшего."""
from gausse.storage.database import count_runs, fetch_runs, open_connection
db_path = tmp_path / "evo_gpu.sqlite3"
@@ -76,9 +90,13 @@ def test_run_evolution_gpu_batch_mode(tmp_path):
)
assert summary["n_evaluated"] == 120
conn = open_connection(db_path)
assert count_runs(conn) >= 120
assert _stats_total(conn) == 120
assert 1 <= count_runs(conn) <= 3 # 1 остров x 3 поколения, только улучшения
rows = fetch_runs(conn, limit=5)
assert all(r.search_mode.startswith("evolve-gpu") for r in rows)
# у сохранённых лучших должна быть посчитана стоимость (GPU-путь
# не считает её для рядовых, но дочитывает для избранных)
assert all(r.cost_rub is not None for r in rows)
def test_run_evolution_islands(tmp_path):
@@ -92,4 +110,6 @@ def test_run_evolution_islands(tmp_path):
)
assert summary["n_evaluated"] == 4 * 15 * 3
conn = open_connection(db_path)
assert count_runs(conn) == 4 * 15 * 3
assert _stats_total(conn) == 4 * 15 * 3
# максимум одно улучшение на остров на поколение
assert 3 <= count_runs(conn) <= 4 * 3