이 게시물은 Chromium 참여자 Ahmed Elwasefi가 Google Summer of Code를 통해 참여자가 된 과정과 발견하여 수정한 접근성 성능 문제를 공유합니다.
카이로의 독일 대학교에서 컴퓨터 공학을 전공하는 마지막 해를 보내던 중 오픈소스에 기여할 기회를 모색하기로 했습니다. Chromium의 초보자 친화적인 문제 목록을 살펴보기 시작했고 특히 접근성에 관심을 갖게 되었습니다. 안내를 찾던 중 Aaron Leventhal을 알게 되었습니다. 그의 전문성과 도움을 주고자 하는 마음에 프로젝트를 위해 그와 팀을 이루게 되었습니다. 이 협업은 Google Summer of Code 경험으로 이어졌으며, 여기서 Chromium 접근성팀과 함께 작업할 수 있었습니다.
Google Summer of Code를 성공적으로 마친 후 성능을 개선하고자 해결되지 않은 스크롤 문제를 계속 해결했습니다. Google의 OpenCollective 프로그램에서 두 번의 지원을 받아 이 프로젝트에 계속 참여하면서 성능 향상을 위해 코드 간소화에 중점을 둔 추가 작업도 진행할 수 있었습니다.
이 블로그 게시물에서는 지난 1년 반 동안 Chromium과 함께한 여정을 공유하고 특히 성능 분야에서 이루어진 기술적 개선사항을 자세히 설명합니다.
접근성 코드가 Chrome의 성능에 미치는 영향
Chrome의 접근성 코드는 스크린 리더와 같은 보조 기술이 웹에 액세스하는 데 도움이 됩니다. 하지만 사용 설정하면 로드 시간, 성능, 배터리 수명에 영향을 줄 수 있습니다. 따라서 필요하지 않은 경우 이 코드는 성능 저하를 방지하기 위해 비활성 상태로 유지됩니다. 약 5~10% 의 사용자는 접근성 코드가 사용 설정되어 있습니다. 이는 주로 플랫폼 접근성 API를 사용하는 비밀번호 관리자 및 바이러스 백신 소프트웨어와 같은 도구로 인해 발생합니다. 이러한 도구는 이러한 API를 사용하여 비밀번호 관리자 및 양식 작성 도구의 비밀번호 필드 찾기와 같이 페이지 콘텐츠와 상호작용하고 이를 수정합니다.
핵심 측정항목의 총적인 성능 저하 정도는 아직 알려지지 않았지만, 최근에 실시한 접근성 자동 사용 중지 (사용하지 않을 때 접근성을 사용 중지) 실험에 따르면 상당히 높습니다. 이 문제는 Chrome의 접근성 코드베이스의 두 가지 주요 영역인 렌더러와 브라우저에서 대량의 계산과 통신이 이루어지기 때문에 발생합니다. 렌더러는 웹 콘텐츠 및 콘텐츠 변경사항에 관한 정보를 수집하여 노드 트리의 접근성 속성을 계산합니다. 그런 다음 변경된 노드는 직렬화되어 파이프를 통해 브라우저 프로세스의 기본 UI 스레드로 전송됩니다. 이 스레드는 이 정보를 수신하여 동일한 노드 트리로 역직렬화한 후 스크린 리더와 같은 서드 파티 보조 기술에 적합한 형식으로 변환합니다.
Chromium 접근성 개선
다음 프로젝트는 Summer of Code 기간에 완료된 후 Google OpenCollective 프로그램의 지원을 받았습니다.
캐시 개선
Chrome에는 DOM 트리를 미러링하는 접근성 트리라는 특수 데이터 구조가 있습니다. 보조 기술이 웹 콘텐츠에 액세스하는 데 사용됩니다. 기기에 이 트리의 정보가 필요한 경우 준비되지 않을 수 있으므로 브라우저는 이러한 요청을 나중에 예약해야 합니다.
이전에는 이 예약이 콜백을 대기열에 배치하는 작업이 포함된 폐쇄라는 메서드를 사용하여 처리되었습니다. 이 접근 방식은 폐쇄가 처리되는 방식으로 인해 추가 작업이 추가되었습니다.
이를 개선하기 위해 enum을 사용하는 시스템으로 전환했습니다. 각 작업에는 특정 enum 값이 할당되며 접근성 트리가 준비되면 해당 작업의 올바른 메서드가 호출됩니다. 이 변경으로 코드를 더 쉽게 이해할 수 있게 되었으며 성능이 20% 이상 개선되었습니다.
스크롤 성능 문제 찾기 및 해결
다음으로, 경계 상자 직렬화를 사용 중지하면 성능이 어떻게 개선되는지 살펴봤습니다. 경계 상자는 웹페이지의 요소 위치와 크기로, 너비, 높이, 상위 요소에 대한 상대 위치와 같은 세부정보를 포함합니다.
이를 테스트하기 위해 경계 상자를 처리하는 코드를 일시적으로 삭제하고 성능 테스트를 실행하여 영향을 확인했습니다. 한 테스트(focus-links.html)에서는 약 1, 618%의 큰 개선이 있었습니다. 이 발견은 후속 작업의 기반이 되었습니다.
느린 테스트 조사
이 특정 테스트가 경계 상자로 인해 느린 이유를 조사하기 시작했습니다. 테스트는 여러 링크에 차례로 포커스를 맞추는 것뿐이었습니다. 따라서 주요 문제는 요소에 초점을 맞추거나 포커스 작업으로 발생한 스크롤입니다. 이를 테스트하기 위해 성능 테스트의 focus()
호출에 {preventScroll: true}
를 추가하여 스크롤을 중지했습니다.
스크롤을 사용 중지하면 경계 상자 계산이 활성화되었을 때 테스트 시간이 1.2밀리초로 감소했습니다. 스크롤이 실제 문제임을 알 수 있었습니다.
focus-links 테스트를 재현하기 위해 scroll-in-page.html이라는 새 테스트를 만들었지만 포커스를 사용하는 대신 scrollIntoView()
를 사용하여 요소를 스크롤합니다. 경계 상자 계산 유무와 관계없이 부드럽게 스크롤하고 즉시 스크롤하는 두 가지를 모두 테스트했습니다.
결과에 따르면 즉시 스크롤과 경계 상자를 사용하면 이 프로세스에 약 66ms가 소요되었습니다. 부드러운 스크롤은 124ms로 더 느렸습니다. 경계 상자를 사용 중지하면 이벤트가 트리거되지 않아 시간이 전혀 걸리지 않았습니다.
케이스는 알고 있었지만 왜 그런지 알 수 없습니다.
이제 스크롤이 접근성 직렬화에 많은 느림의 원인이라는 것을 알게 되었지만 그 이유를 알아야 했습니다. 이를 분석하기 위해 perf 및 pprof라는 두 가지 도구를 사용하여 브라우저 프로세스에서 실행된 작업을 분류했습니다. 이러한 도구는 C++에서 프로파일링하는 데 자주 사용됩니다. 다음 그래프는 흥미로운 부분의 스니펫을 보여줍니다.
조사 결과 문제는 직렬화 해제 코드 자체가 아니라 호출 빈도에 있는 것으로 확인되었습니다. 이를 이해하려면 Chromium에서 접근성 업데이트가 작동하는 방식을 살펴봐야 합니다. 업데이트는 개별적으로 전송되지 않습니다. 대신 모든 속성을 저장하는 AXObjectCache
라는 중앙 위치가 있습니다. 노드가 변경되면 다양한 메서드가 캐시에게 알림을 보내 나중에 직렬화할 때 해당 노드를 더티로 표시합니다. 그런 다음 변경되지 않은 속성을 비롯하여 변경된 메모의 모든 속성이 직렬화되어 브라우저로 전송됩니다. 이 설계는 단일 업데이트 경로를 사용하여 코드를 단순화하고 복잡성을 줄이지만 스크롤과 같은 '더티로 표시' 이벤트가 빠르게 발생하면 속도가 느려집니다. scrollX
및 scrollY
값만 변경되지만 매번 나머지 속성을 함께 직렬화합니다. 업데이트 속도는 초당 20회 이상에 달했습니다.
경계 상자 직렬화는 경계 상자 세부정보만 전송하는 더 빠른 직렬화 경로를 사용하여 이 문제를 해결하므로 다른 속성에 영향을 주지 않고 빠르게 업데이트할 수 있습니다. 이 메서드는 경계 상자 변경사항을 효율적으로 처리합니다.
스크롤 수정
해결 방법은 명확했습니다. 경계 상자 직렬화를 통해 현재 스크롤 오프셋을 포함합니다. 이렇게 하면 스크롤 업데이트가 빠른 경로를 통해 처리되므로 불필요한 지연 없이 성능이 향상됩니다. 스크롤 오프셋을 경계 상자 데이터로 패킹하여 더 원활하고 효율적인 업데이트를 위해 프로세스를 최적화하여 접근성 기능을 사용 설정한 사용자에게 더 원활한 환경을 제공합니다. 수정사항을 구현한 후 스크롤 테스트에서 최대 825%가 개선되었습니다.
코드 간소화
이 기간 동안 저는 레이어에 불필요하게 퍼져 있는 코드를 줄이거나 삭제하여 코드를 단순화하는 Onion Soup라는 프로젝트의 일환으로 코드 품질에 중점을 두었습니다.
첫 번째 프로젝트는 접근성 데이터가 렌더러에서 브라우저로 직렬화되는 방식을 간소화하는 것을 목표로 했습니다. 이전에는 데이터가 대상으로 도달하기 전에 추가 레이어를 통과해야 했으며, 이로 인해 불필요한 복잡성이 추가되었습니다. 데이터를 직접 전송하여 중간자를 배제함으로써 이 프로세스를 간소화했습니다.
또한 레이아웃이 완료될 때 트리거되는 이벤트와 같이 시스템에서 불필요한 작업을 일으키는 오래된 이벤트를 식별하고 삭제했습니다. 이러한 솔루션을 더 효율적인 솔루션으로 대체했습니다.
그 밖에도 몇 가지 사소한 개선사항이 적용되었습니다. 안타깝게도 성능 개선은 기록되지 않았지만 코드가 이전보다 훨씬 명확해지고 자체 문서화되었다는 점을 알려드립니다. 이렇게 하면 향후 성능 개선을 위한 길을 닦는 데 큰 도움이 됩니다. gerrit 프로필에서 실제 변경사항을 확인할 수 있습니다.
결론
Chromium 접근성팀과 함께 일한 것은 보람 있는 여정이었습니다. 스크롤 성능 최적화에서 코드베이스 단순화까지 다양한 문제를 해결하면서 대규모 프로젝트의 개발에 대해 더 깊이 이해하고 프로파일링을 위한 중요한 도구를 배웠습니다. 또한 모든 사용자를 포용하는 웹을 만드는 데 접근성이 얼마나 중요한지 알게 되었습니다. 이번 개선사항은 보조 기술을 사용하는 사용자의 사용자 환경을 개선할 뿐만 아니라 브라우저의 전반적인 성능과 효율성에도 기여합니다.
실적 결과는 인상적이었습니다. 예를 들어 작업 예약에 enum을 사용하도록 전환하면 성능이 20%이상 향상되었습니다. 또한 스크롤 수정으로 인해 스크롤 테스트가 최대 825% 감소했습니다. 코드 단순화 변경사항으로 코드가 더 명확하고 유지보수하기 쉬워졌을 뿐만 아니라 향후 개선을 위한 기반도 마련되었습니다.
한 해 동안 지원과 안내를 제공해 주신 Stefan Zager, Chris Harrelson, Mason Freed님, 특히 이 기회를 제공해 주신 Aaron Leventhal님께 감사드립니다. 또한 Tab Atkins-Bittner님과 GSoC팀의 지원에 감사드립니다.
의미 있는 프로젝트에 참여하고 기술을 개발하고자 하는 경우 Chromium에 참여하는 것이 좋습니다. 이는 학습에 좋은 방법이며 Google Summer of Code와 같은 프로그램은 여정을 시작하는 데 좋은 출발점이 됩니다.