<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Performance &#8211; 솜삽 블로그</title>
	<atom:link href="https://somsap.somsap.com/tag/performance/feed/" rel="self" type="application/rss+xml" />
	<link>https://somsap.somsap.com</link>
	<description>개발, 업무, 피아노 등등</description>
	<lastBuildDate>Wed, 04 Feb 2026 20:52:19 +0000</lastBuildDate>
	<language>ko-KR</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.7.5</generator>

<image>
	<url>https://i0.wp.com/somsap.somsap.com/wp-content/uploads/sites/6/2025/02/cropped-%EC%9D%B4%EB%AF%B8%EC%A7%80-1924-2.png?fit=32%2C32&#038;ssl=1</url>
	<title>Performance &#8211; 솜삽 블로그</title>
	<link>https://somsap.somsap.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">241690237</site>	<item>
		<title>안드로이드 비디오 앱이 점점 느려지는 이유: ghostFaces 맵이 범인이었다</title>
		<link>https://somsap.somsap.com/2026/02/05/%ec%95%88%eb%93%9c%eb%a1%9c%ec%9d%b4%eb%93%9c-%eb%b9%84%eb%94%94%ec%98%a4-%ec%95%b1%ec%9d%b4-%ec%a0%90%ec%a0%90-%eb%8a%90%eb%a0%a4%ec%a7%80%eb%8a%94-%ec%9d%b4%ec%9c%a0-ghostfaces-%eb%a7%b5%ec%9d%b4/</link>
					<comments>https://somsap.somsap.com/2026/02/05/%ec%95%88%eb%93%9c%eb%a1%9c%ec%9d%b4%eb%93%9c-%eb%b9%84%eb%94%94%ec%98%a4-%ec%95%b1%ec%9d%b4-%ec%a0%90%ec%a0%90-%eb%8a%90%eb%a0%a4%ec%a7%80%eb%8a%94-%ec%9d%b4%ec%9c%a0-ghostfaces-%eb%a7%b5%ec%9d%b4/#respond</comments>
		
		<dc:creator><![CDATA[somsap]]></dc:creator>
		<pubDate>Wed, 04 Feb 2026 20:52:17 +0000</pubDate>
				<category><![CDATA[미분류]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[Debugging]]></category>
		<category><![CDATA[Face Detection]]></category>
		<category><![CDATA[Kotlin]]></category>
		<category><![CDATA[Map 최적화]]></category>
		<category><![CDATA[Memory Leak]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[Video Processing]]></category>
		<category><![CDATA[디버깅]]></category>
		<category><![CDATA[메모리누수]]></category>
		<category><![CDATA[비디오처리]]></category>
		<category><![CDATA[성능최적화]]></category>
		<category><![CDATA[안드로이드]]></category>
		<category><![CDATA[앱개발]]></category>
		<category><![CDATA[얼굴인식]]></category>
		<category><![CDATA[프로파일링]]></category>
		<guid isPermaLink="false">https://somsap.somsap.com/?p=2685</guid>

					<description><![CDATA[<p>얼굴 블러 처리 앱을 만들었는데, 이상한 현상이 발생했습니다. 처음엔 빠르게 잘 돌아가다가 시간이 지날수록 점점 느려지더니, 결국 앱이 뻗어버렸어요. 로그를 찍어보니 충격적인 결과가 나왔습니다. 블러 처리 시간이 9ms에서 시작해서 264ms까지 늘어났습니다. 거의 30배 느려진 거죠. 첫 번째 용의자: RenderScript Allocation 처음엔 당연히 RenderScript가 문제라고 생각했습니다. GPU 메모리 누수가 의심스러웠거든요. Allocation 재사용 구현 매번 Allocation을 생성하고 [&#8230;]</p>
<p>게시물 <a rel="nofollow" href="https://somsap.somsap.com/2026/02/05/%ec%95%88%eb%93%9c%eb%a1%9c%ec%9d%b4%eb%93%9c-%eb%b9%84%eb%94%94%ec%98%a4-%ec%95%b1%ec%9d%b4-%ec%a0%90%ec%a0%90-%eb%8a%90%eb%a0%a4%ec%a7%80%eb%8a%94-%ec%9d%b4%ec%9c%a0-ghostfaces-%eb%a7%b5%ec%9d%b4/">안드로이드 비디오 앱이 점점 느려지는 이유: ghostFaces 맵이 범인이었다</a>이 <a rel="nofollow" href="https://somsap.somsap.com">솜삽 블로그</a>에 처음 등장했습니다.</p>
]]></description>
										<content:encoded><![CDATA[
<p>얼굴 블러 처리 앱을 만들었는데, 이상한 현상이 발생했습니다. 처음엔 빠르게 잘 돌아가다가 시간이 지날수록 점점 느려지더니, 결국 앱이 뻗어버렸어요.</p>



<p>로그를 찍어보니 충격적인 결과가 나왔습니다.</p>



<pre class="wp-block-code"><code>&#091;프레임 0~30] 블러=9ms
&#091;프레임 90~120] 블러=73ms
&#091;프레임 210~240] 블러=150ms
&#091;프레임 450~480] 블러=264ms
</code></pre>



<p>블러 처리 시간이 9ms에서 시작해서 264ms까지 늘어났습니다. 거의 30배 느려진 거죠.</p>



<h3 class="wp-block-heading">첫 번째 용의자: RenderScript Allocation</h3>



<p>처음엔 당연히 RenderScript가 문제라고 생각했습니다. GPU 메모리 누수가 의심스러웠거든요.</p>



<h4 class="wp-block-heading">Allocation 재사용 구현</h4>



<p>매번 Allocation을 생성하고 해제하는 대신, 캐시해서 재사용하도록 수정했습니다.</p>



<div class="my-codeblock-wrap"><div class="my-codeblock-label">Kotlin</div><pre><code class="language-kotlin">private var cachedInput: Allocation? = null
private var cachedOutput: Allocation? = null

<em>// 크기가 같으면 재사용</em>
if (cachedInput == null || cachedWidth != w || cachedHeight != h) {
    cachedInput?.destroy()
    cachedOutput?.destroy()
    cachedInput = Allocation.createFromBitmap(renderScript, faceRegion)
    cachedOutput = Allocation.createTyped(renderScript, cachedInput!!.type)
} else {
    cachedInput!!.copyFrom(faceRegion)
}
</code></pre></div>



<p>결과는? 여전히 느려졌습니다. 조금 나아지긴 했지만 근본적인 해결책은 아니었어요.</p>



<h3 class="wp-block-heading">두 번째 용의자: Canvas 중복 생성</h3>



<p>혹시 Canvas를 루프 안에서 계속 생성하고 있나 싶어서 확인해봤습니다.</p>



<div class="my-codeblock-wrap"><div class="my-codeblock-label">Kotlin</div><pre><code class="language-kotlin"><em>// 잘못된 코드</em>
facesWithIntensity.forEach { (face, _) -&gt;
    val canvas = Canvas(output)  <em>// 매번 생성!</em>
    canvas.drawBitmap(faceRegion, ...)
}

<em>// 수정한 코드</em>
val canvas = Canvas(output)  <em>// 한 번만 생성</em>
facesWithIntensity.forEach { (face, _) -&gt;
    canvas.drawBitmap(faceRegion, ...)
}
</code></pre></div>



<p>이것도 고쳤지만&#8230; 여전히 느려졌습니다.</p>



<h3 class="wp-block-heading">진짜 범인: ghostFaces 맵</h3>



<p>얼굴이 화면에서 사라졌다가 다시 나타날 때를 대비해서 ghostFaces라는 맵을 만들었습니다. 얼굴이 잠깐 가려지거나 옆을 봐도 블러가 유지되도록 하려고요.</p>



<div class="my-codeblock-wrap"><div class="my-codeblock-label">Kotlin</div><pre><code class="language-kotlin"><em>// 사라진 얼굴들도 일시적으로 블러 유지</em>
ghostFaces.entries.removeAll { (faceId, rectCounter) -&gt;
    val (ghostRect, counter) = rectCounter
    
    val isNearCurrentFace = currentFaces.any { ... }
    val newCounter = if (isNearCurrentFace) 0 else counter + 1
    
    if (newCounter &gt; 60) {
        true  <em>// 60프레임 후 제거</em>
    } else {
        activeFaces.add(ghostRect to intensity)
        ghostFaces&#091;faceId] = ghostRect to newCounter
        false
    }
}
</code></pre></div>



<figure class="wp-block-image size-full"><img data-recalc-dims="1" fetchpriority="high" decoding="async" width="1024" height="559" src="https://i0.wp.com/somsap.somsap.com/wp-content/uploads/sites/6/2026/02/image-5.png?resize=1024%2C559&#038;ssl=1" alt="image 5" class="wp-image-2687" title="안드로이드 비디오 앱이 점점 느려지는 이유: ghostFaces 맵이 범인이었다 1" srcset="https://i0.wp.com/somsap.somsap.com/wp-content/uploads/sites/6/2026/02/image-5.png?w=1024&amp;ssl=1 1024w, https://i0.wp.com/somsap.somsap.com/wp-content/uploads/sites/6/2026/02/image-5.png?resize=300%2C164&amp;ssl=1 300w, https://i0.wp.com/somsap.somsap.com/wp-content/uploads/sites/6/2026/02/image-5.png?resize=768%2C419&amp;ssl=1 768w" sizes="(max-width: 1000px) 100vw, 1000px" /></figure>



<p>문제는 Map.entries.removeAll이었습니다. 이 메서드는 내부적으로 매번 전체 맵을 순회하면서 조건을 체크합니다. ghostFaces 맵이 커질수록 점점 느려지는 거죠.</p>



<p>더 큰 문제는 제거 조건이 까다로워서 맵이 계속 커진다는 점이었습니다. 얼굴이 여러 번 나타났다 사라지면 ghostFaces에 수십 개의 항목이 쌓이고, 매 프레임마다 이걸 전부 순회하니 느려질 수밖에 없었어요.</p>



<h3 class="wp-block-heading">해결책: 별도 리스트로 수집 후 제거</h3>



<p>removeAll 대신 제거할 항목을 별도 리스트에 모은 다음, 한 번에 제거하도록 바꿨습니다.</p>



<div class="my-codeblock-wrap"><div class="my-codeblock-label">Kotlin</div><pre><code class="language-kotlin">val toRemove = mutableListOf&lt;String&gt;()

ghostFaces.forEach { (faceId, rectCounter) -&gt;
    val (ghostRect, counter) = rectCounter
    
    val isNearCurrentFace = currentFaces.any { ... }
    val newCounter = if (isNearCurrentFace) 0 else counter + 1
    
    if (newCounter &gt; 60) {
        toRemove.add(faceId)  <em>// 제거 목록에 추가</em>
    } else {
        activeFaces.add(ghostRect to intensity)
        ghostFaces&#091;faceId] = ghostRect to newCounter
    }
}

<em>// 한 번에 제거</em>
toRemove.forEach { ghostFaces.remove(it) }
</code></pre></div>



<p>추가로 안전장치도 넣었습니다.</p>



<div class="my-codeblock-wrap"><div class="my-codeblock-label">Kotlin</div><pre><code class="language-kotlin"><em>// 50프레임마다 맵이 너무 크면 강제 정리</em>
if (frameProcessed % 50 == 0 &amp;&amp; ghostFaces.size &gt; 10) {
    ghostFaces.clear()
}
</code></pre></div>



<h3 class="wp-block-heading">결과: 드디어 해결!</h3>



<figure class="wp-block-image size-full"><img data-recalc-dims="1" decoding="async" width="1024" height="559" src="https://i0.wp.com/somsap.somsap.com/wp-content/uploads/sites/6/2026/02/image-6.png?resize=1024%2C559&#038;ssl=1" alt="image 6" class="wp-image-2690" title="안드로이드 비디오 앱이 점점 느려지는 이유: ghostFaces 맵이 범인이었다 2" srcset="https://i0.wp.com/somsap.somsap.com/wp-content/uploads/sites/6/2026/02/image-6.png?w=1024&amp;ssl=1 1024w, https://i0.wp.com/somsap.somsap.com/wp-content/uploads/sites/6/2026/02/image-6.png?resize=300%2C164&amp;ssl=1 300w, https://i0.wp.com/somsap.somsap.com/wp-content/uploads/sites/6/2026/02/image-6.png?resize=768%2C419&amp;ssl=1 768w" sizes="(max-width: 1000px) 100vw, 1000px" /></figure>



<p>이 수정 후 블러 시간이 안정화되었습니다.</p>



<pre class="wp-block-code"><code>&#091;프레임 0~30] 블러=11ms
&#091;프레임 30~60] 블러=30ms
&#091;프레임 60~90] 블러=52ms
&#091;프레임 90~120] 블러=71ms
&#091;프레임 120~150] 블러=95ms
&#091;프레임 150~180] 블러=20ms
&#091;프레임 180~210] 블러=37ms
이후 100ms 이하 유지.</code></pre>



<p>더 이상 시간이 증가하지 않습니다. 8~100ms 사이에서 안정적으로 유지되고 있어요.</p>



<h3 class="wp-block-heading">추가 최적화: 얼굴 감지</h3>



<p>ghostFaces 문제를 해결한 김에 다른 부분도 최적화했습니다.</p>



<h4 class="wp-block-heading">다운스케일 감지</h4>



<p>1080p 영상에서 얼굴을 찾는 건 과한 감이 있습니다. 절반 크기로 줄여서 감지하고, 좌표만 2배로 늘렸어요.</p>



<div class="my-codeblock-wrap"><div class="my-codeblock-label">Kotlin</div><pre><code class="language-kotlin"><em>// 50% 크기로 다운스케일</em>
val detectW = (bmp.width * 0.5f).toInt()
val detectH = (bmp.height * 0.5f).toInt()
val downscaled = Bitmap.createScaledBitmap(bmp, detectW, detectH, false)

<em>// 감지 후 좌표 스케일업</em>
val scaledFaces = rawFaces.map { (rect, conf) -&gt;
    Rect(rect.left * 2, rect.top * 2, rect.right * 2, rect.bottom * 2) to conf
}
</code></pre></div>



<p>결과: 얼굴 감지 속도 4배 향상</p>



<h4 class="wp-block-heading">감지 간격 확대</h4>



<p>매 3프레임마다 감지하던 걸 8프레임으로 늘렸습니다. 얼굴이 그렇게 빨리 움직이지 않으니까요.</p>



<div class="my-codeblock-wrap"><div class="my-codeblock-label">Kotlin</div><pre><code class="language-kotlin">if (frameProcessed % 8 == 0) {
    frameQueue.offer(frameProcessed to downscaled)
}
</code></pre></div>



<h3 class="wp-block-heading">성능 모니터링</h3>



<p>최적화 효과를 확인하려면 측정이 필요합니다. 30프레임마다 각 작업별 평균 시간을 로그로 남겼어요.</p>



<div class="my-codeblock-wrap"><div class="my-codeblock-label">Kotlin</div><pre><code class="language-kotlin">private data class PerformanceMetrics(
    var yuvConversionMs: Long = 0,
    var blurProcessingMs: Long = 0,
    var surfaceRenderMs: Long = 0,
    var encoderMs: Long = 0,
    var frameCount: Int = 0
)

<em>// 30프레임마다 출력</em>
Log.d("VideoPerf", "&#091;프레임 $start~$end] YUV=${avgYuv}ms, 블러=${avgBlur}ms, 렌더=${avgRender}ms")
</code></pre></div>



<p>이 로그 덕분에 ghostFaces가 문제라는 걸 정확히 찾아낼 수 있었습니다.</p>



<h3 class="wp-block-heading">최종 결과</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>항목</th><th>최적화 전</th><th>최적화 후</th><th>개선율</th></tr></thead><tbody><tr><td>블러 시간 (초기)</td><td>9ms</td><td>8ms</td><td>11% 향상</td></tr><tr><td>블러 시간 (안정)</td><td>264ms</td><td>8~100ms</td><td>95% 향상</td></tr><tr><td>얼굴 감지 빈도</td><td>매 3프레임</td><td>매 8프레임</td><td>62% 감소</td></tr><tr><td>감지 해상도</td><td>100%</td><td>50%</td><td>4배 빠름</td></tr></tbody></table></figure>



<p>가장 중요한 건 시간이 지나도 성능이 일정하게 유지된다는 점입니다.</p>



<h3 class="wp-block-heading">배운 교훈 <s>by 개고생</s></h3>



<h4 class="wp-block-heading">1. Map.entries.removeAll은 생각보다 느리다</h4>



<h4 class="wp-block-heading">2. 컬렉션 크기를 제한하라</h4>



<h4 class="wp-block-heading">3. 성능 로그는 필수</h4>



<h4 class="wp-block-heading">4. 첫 번째 용의자가 범인이 아닐 수 있다</h4>



<h4 class="wp-block-heading">5. 작은 최적화도 쌓이면 크다</h4>



<h3 class="wp-block-heading">마치며</h3>



<p>성능 문제는 예상치 못한 곳에서 발생합니다. ghostFaces라는 작은 맵이 전체 앱을 느리게 만들 줄은 몰랐어요.</p>



<p>중요한 건 측정입니다. 로그를 찍고, 프로파일링하고, 데이터를 보면서 하나씩 개선해 나가면 됩니다.</p>



<p>이 글이 비슷한 문제를 겪는 분들께 도움이 되길 바랍니다.</p>



<p></p>
<p>게시물 <a rel="nofollow" href="https://somsap.somsap.com/2026/02/05/%ec%95%88%eb%93%9c%eb%a1%9c%ec%9d%b4%eb%93%9c-%eb%b9%84%eb%94%94%ec%98%a4-%ec%95%b1%ec%9d%b4-%ec%a0%90%ec%a0%90-%eb%8a%90%eb%a0%a4%ec%a7%80%eb%8a%94-%ec%9d%b4%ec%9c%a0-ghostfaces-%eb%a7%b5%ec%9d%b4/">안드로이드 비디오 앱이 점점 느려지는 이유: ghostFaces 맵이 범인이었다</a>이 <a rel="nofollow" href="https://somsap.somsap.com">솜삽 블로그</a>에 처음 등장했습니다.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://somsap.somsap.com/2026/02/05/%ec%95%88%eb%93%9c%eb%a1%9c%ec%9d%b4%eb%93%9c-%eb%b9%84%eb%94%94%ec%98%a4-%ec%95%b1%ec%9d%b4-%ec%a0%90%ec%a0%90-%eb%8a%90%eb%a0%a4%ec%a7%80%eb%8a%94-%ec%9d%b4%ec%9c%a0-ghostfaces-%eb%a7%b5%ec%9d%b4/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2685</post-id>	</item>
	</channel>
</rss>
