Я написал мягкое приложение реального времени в Haskell, которое занимается имитируемой физикой, обнаружением столкновения, всем этим хорошим материалом. Выполняя все это, я выделяю много памяти, и я мог бы, вероятно, оптимизировать использование памяти, если захочу, но так как я хорошо сижу на 40% -ном процессоре и только в 1% используемой ОЗУ, это не кажется необходимым. То, что я вижу, - это то, что много времени, когда сборщик мусора заходит, рамки пропускаются. Я проверял, что это является причиной проблемы путем профилирования с помощью threadscope
: никаких полезных вычислений не происходит иногда до 0,05 секунд, пока сборщик мусора ведет свою деятельность, в результате чего получается до 3 пропущенных кадров, что очень заметно и очень раздражает.
Теперь я попытался решить это, вручную вызвав performMinorGC
каждый фрейм, и это, похоже, облегчает проблему, делая ее более гладкой, за исключением того, что общее использование ЦП резко увеличивается примерно до 70%. Ясно, что я предпочел бы избежать этого.
Еще одна вещь, которую я пробовал, заключалась в уменьшении пространства распределения GC до 64k от 512k с -H64k, и я также попытался установить -I0.03, чтобы попытаться собрать его чаще. Обе эти опции изменили структуру сборки мусора, которую я увидел в threadscope
, но они все же привели к пропущенным кадрам.
Может ли кто-нибудь с опытом работы с оптимизацией GC помочь мне здесь? Я обречен на ручное вызов performMinorGC
и потерю массивной потери производительности?
ИЗМЕНИТЬ
Я попытался запустить его в течение такого же количества времени в этих тестах, но так как в режиме реального времени нет точки, в которой он "сделан".
Статистика времени выполнения с performMinorGC
каждые 4 кадра:
9,776,109,768 bytes allocated in the heap
349,349,800 bytes copied during GC
53,547,152 bytes maximum residency (14 sample(s))
12,123,104 bytes maximum slop
105 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 15536 colls, 15536 par 3.033s 0.997s 0.0001s 0.0192s
Gen 1 14 colls, 13 par 0.207s 0.128s 0.0092s 0.0305s
Parallel GC work balance: 6.15% (serial 0%, perfect 100%)
TASKS: 20 (2 bound, 13 peak workers (18 total), using -N4)
SPARKS: 74772 (20785 converted, 0 overflowed, 0 dud, 38422 GC'd, 15565 fizzled)
INIT time 0.000s ( 0.001s elapsed)
MUT time 9.773s ( 7.368s elapsed)
GC time 3.240s ( 1.126s elapsed)
EXIT time 0.003s ( 0.004s elapsed)
Total time 13.040s ( 8.499s elapsed)
Alloc rate 1,000,283,400 bytes per MUT second
Productivity 75.2% of total user, 115.3% of total elapsed
gc_alloc_block_sync: 29843
whitehole_spin: 0
gen[0].sync: 11
gen[1].sync: 71
Без performMinorGC
12,316,488,144 bytes allocated in the heap
447,495,936 bytes copied during GC
63,556,272 bytes maximum residency (15 sample(s))
15,418,296 bytes maximum slop
146 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 19292 colls, 19292 par 2.613s 0.950s 0.0000s 0.0161s
Gen 1 15 colls, 14 par 0.237s 0.165s 0.0110s 0.0499s
Parallel GC work balance: 2.67% (serial 0%, perfect 100%)
TASKS: 17 (2 bound, 13 peak workers (15 total), using -N4)
SPARKS: 100714 (29688 converted, 0 overflowed, 0 dud, 47577 GC'd, 23449 fizzled)
INIT time 0.000s ( 0.001s elapsed)
MUT time 13.377s ( 9.917s elapsed)
GC time 2.850s ( 1.115s elapsed)
EXIT time 0.000s ( 0.006s elapsed)
Total time 16.247s ( 11.039s elapsed)
Alloc rate 920,744,995 bytes per MUT second
Productivity 82.5% of total user, 121.4% of total elapsed
gc_alloc_block_sync: 68533
whitehole_spin: 0
gen[0].sync: 9
gen[1].sync: 147
Общая производительность, по-видимому, ниже, чем performMinorGC
, чем когда я вчера ее тестировал по-настоящему - до того, как она была всегдa > 90%.