Archive Lab 06. 사진이 비는 구간에 네이티브 로그를 붙였다 글 표지
Archive LabReact Native성능 측정Android

Archive Lab 06. 사진이 비는 구간에 네이티브 로그를 붙였다

이미지 뷰의 연결부터 로드와 첫 그리기까지 같은 카드로 연결했습니다. 계측의 판정과 순서를 고치고, 비트맵이 준비되기 전후를 구분한 기록입니다.

지난 글에서는 빠른 스크롤 중 사진이 회색으로 보이는 장면을 확인했다. 이미지 로드 이벤트도 모았지만, JS 콜백을 받은 시각만으로는 사진이 언제 그려졌는지 알 수 없었다.

이번에는 Android 쪽에 기록을 붙였다. 사진을 받았다는 이벤트와 그리기 호출을 같은 카드로 연결해 보고 싶었다. 개인 갤럭시에서 자료 1,000개를 띄웠고, 사진은 앞선 실험과 같은 로컬 이미지 12장을 반복했다.

카드 번호를 네이티브 뷰까지 가져갔다

진단 빌드의 이미지에 nativeID를 붙였다. 카드 번호와 이미지 번호, 목록인지 상세 화면인지를 구분하는 값이다. 같은 카드가 다시 만들어지거나 화면에 붙었다 떨어지는 경우도 따로 기록했다.

이미지 뷰가 창에 연결될 때, 기존 로드 이벤트가 발행될 때, 이미지 계층의 그리기 코드가 호출될 때를 기록했다. 그리기는 Fresco 이미지 계층의 빈 오버레이 자리에 Drawable을 넣어 관찰했다. 이 Drawable은 아무것도 그리지 않아 사진 위에 표식이 보이지는 않는다.

이 콜백을 화면 표시 완료 시각으로 부르지는 않았다. Android는 그리기 명령을 기록해 두었다가 재사용할 수 있다. 내 코드가 호출됐다는 사실과 사용자가 그 픽셀을 봤다는 사실은 구분해야 한다. 계측 자체에도 비용이 있으므로 이 앱의 수치를 이전 프레임 성능 측정에 섞지 않았다. Android의 그리기 모델

hasImage()부터 확인해야 했다

사진이 있는지 확인하려고 처음에는 Fresco의 hasImage()를 읽었다. 그런데 로드 완료 전에도 참이 나왔다.

설치된 Fresco 3.6.0 소스를 보니 이 메서드는 현재 이미지 쪽 drawable이 처음의 빈 drawable과 같은 객체인지 비교하고 있었다. RN이 모서리 설정을 다시 적용하면서 빈 색상 drawable이 다른 객체로 감싸지면, 아직 사진이 없어도 참이 될 수 있었다.

추측으로 끝내지 않으려고 실제 Android/Fresco 객체로 작은 검사를 만들었다. 빈 이미지 계층을 만들고, 모서리 설정을 적용하고, 작은 비트맵을 넣었다가 다시 비웠다.

상태hasImage()비트맵 존재
처음 만든 빈 상태거짓없음
빈 상태에 모서리 설정 재적용없음
비트맵 추가있음
다시 초기화거짓없음

실기기에서도 이 순서를 확인했다. 이후에는 drawable 계층에서 실제 비트맵 객체를 찾도록 바꿨다. 이번 앱의 정적인 JPEG 사진을 검사하는 방식이라, 비트맵을 찾아도 화면에 표시됐는지까지는 알 수 없다. Fresco의 구현

이벤트 순서를 기록하는 코드도 한 번 고쳤다. runOnUiThread로 넘기면 이미 UI 스레드에 있을 때 바로 실행될 거라고 생각했는데, RN 0.86.3의 이 함수는 항상 큐에 넣었다. 그 사이 다음 그리기가 진행되면서 내가 만든 기록의 순서가 달라졌다. 이미 UI 스레드라면 그 자리에서 기록하도록 바꾸고, 이 순서를 확인하는 테스트를 추가했다. 사용 중인 RN 구현

11번 카드의 첫 등장을 따라갔다

계측을 고친 뒤 빠른 왕복 스크롤을 두 번 수집했다. 기록은 스크롤 중 메모리에 쌓고 끝난 뒤 내보냈다. 시작·끝 표식, 순번, 전체 개수를 검사했고 두 수집 모두 누락이 없었다. 앞선 실행 중 내보내기가 잘렸거나 판정·순서에 문제가 있던 기록은 결과에서 제외했다.

첫 번째 정상 수집의 11번 카드에서 이미지 요청 시작을 0ms로 놓으면 다음과 같았다.

요청 시작 후기록한 일
0ms이미지 요청 시작
2ms보이는 영역의 그리기 호출. 비트맵은 아직 없음
19ms이미지 로드 완료 이벤트
25ms비트맵이 있는 상태의 그리기 호출

다음 수집에서도 같은 카드의 첫 요청은 2ms에 비트맵 없는 그리기, 24ms에 로드 완료, 28ms에 비트맵이 있는 그리기 순서였다.

이 카드에서는 로드 완료 전에 이미지 계층을 그리는 호출이 들어왔다. 로드가 끝난 뒤에도 다음 그리기 호출까지 간격이 있었다. 지난번보다 사진이 채워지는 과정을 한 단계 더 좁혀 볼 수 있었다.

기다리며 몸을 움직이는 미스터 빈
다 됐다면서요.

다만 25ms나 28ms가 사용자가 본 빈칸의 지속시간은 아니다. 기록한 것은 네이티브 이벤트와 그리기 호출이고, 영상의 시각과 정확하게 맞춘 것도 아니다. 같은 이유로 이 숫자를 화면 지연이나 FPS로 환산하지 않았다.

다음에는 사진을 준비하는 시점을 바꿔볼 차례

11번 카드의 네이티브 뷰는 요청 전부터 있었지만 사진은 아직 준비되지 않았다. 이 경우에는 뷰가 창에 연결되는 과정에서 요청이 시작됐다. 화면 밖에 뷰를 미리 만들어 두어도 첫 사진까지 준비되는 건 아니었다.

이제 다음 후보는 처음 등장할 사진을 조금 일찍 준비하는 방식이다. 빈 사진 영역이 줄어드는지 확인하면서 메모리와 스크롤 지표도 같이 보려고 한다. 미리 하는 작업을 늘린 만큼 다른 비용이 생길 수 있기 때문이다.

이번에는 일반 앱의 사진 표시 방식을 바꾸지 않았다. 진단을 끈 앱을 기기에 다시 설치하고 검색·즐겨찾기·상세 화면도 확인했다. 다음 변경을 비교할 때는 로드 완료 콜백뿐 아니라, 비트맵이 없는 상태로 그리기가 들어오는지도 함께 확인할 수 있게 됐다.