애드온 성능 설계

 

최근 Internet Explorer 8 Beta 1 출시를 위해 개발중인 IE 팀은 성능에 심혈을 기울이고 있습니다. IE  향상을 위한 노력의 일환으로 실행한 조사에서, 몇가지 애드온에 성능상의 문제가 있다는 것을 알았습니다. 이 글에서는 우리가 발견한 공통의 테마를 이야기합니다.

우선 먼저 이 블로그에 관한 피드백을 IE Beta 뉴스 그룹이나 around the web (영어) 를 통해 주신 여러분에게 감사합니다. Internet Explorer 팀은 IE8 의 성능에 관해서 열심히 노력을 계속하고 있으며, 초기 단계에서 성과로 이어진 것을 매우 기쁘게 생각합니다. 아직 개선의 여지 (와 계획)는 있지만, IE8 Beta1 의 성능 개선에 관한 자세한 내용은 개발자 백서 (영어)를 참조해 주세요.

IE 의 애드온 개발이 처음으로, 참조가 필요한 경우에는 다음의 몇가지 링크에서 시작하는 것을 권장합니다.

대략적으로 말하면, 애드온 성능 문제는 일반적으로 두가지 영역에서 IE 사용자에 영향을 줍니다.

  1. IE 윈도우 또는 각각의 탭 열기/닫기
  2. 브라우저 반응

윈도우나 탭이 열리고 닫히는 속도는 생성될 때마다 부하가 높은 일을 많이 실행하는 애드온에 크게 영향을 받습니다. 특별히 공통된 문제의 하나는 브라우저를 실행하거나 종료하는 동안에 애드온이 업데이트를 확인하는 것입니다.

레지스트리를 잘못 사용하면 반응을 느리게 만듭니다. 이것은 공통된 문제의 하나입니다. 애드온의 상당수는 부하가 높은 레지스트리 조작을 실행하지만, 이것은 Internet Explorer 반응을 저하시킬 가능성이 있습니다.

다음에서 이 두가지 영역을 설명하고, 애드온 성능을 설계할 때의 지침을 몇가지 소개합니다.

애드온 초기화와 업데이트 확인
  • 원칙 1 : 무리하지 않음 : 힘든 일은 다른 스레드에 맡김
  • 원칙 2 : 부스에 도착한 후 요금을 지불

시작할 때, Internet Explorer 는 레지스트리를 조사하여, 설치된 애드온을 확인합니다. 브라우저 도우미 개체 또는 도구 막대가 설치된 것을 IE 가 검색하면 CoCreateInstance 를 호출하여, 설치되어 유효한 애드온을 인스턴스화합니다. Internet Explorer 는 기본적으로 애드온을 인 프로세스 서버로서 생성하여, IE 의 메인 UI 스레드 중에서 실행합니다. 하위호환성을 위해 Internet Explorer 는 열린 각 탭에 대해서 이러한 단계에 따릅니다. 이 동작은 몇가지 이유 때문에 중요합니다. 그 이유는 애드온이 직면한 가장 일반적인 문제를 설명하면 이해하실 수 있을 것입니다.

무리를 하지 않는 : 힘든 일은 다른 스레드에 맡긴다

일반적으로 많은 애드온에서 공통된 경향의 하나는 온라인 컨텐츠와의 통합입니다. 라이브 데이터에 관해서 이 통합화를 유지하려면 항상 업데이트 메커니즘이 필요합니다. 조사한 결과에 따르면 많은 경우에 초기화 동안에 IE 가 제어를 애드온 SetSite 구현에 전달할 때, 애드온은 동기 업데이트 확인을 실행합니다. 다음은 Internet Explorer에서 애드온이 초기화되는 방법에 대한 설명으로, 이러한 종류의 업데이트 확인이 어떤 영향을 미치는지를 추측할 수 있습니다. 다음과 같은 흐름을 생각해 주세요.

  1. IE 가 초기화 시작
  2. Foo 도구 막대가 설치된 것을 IE 가 검색
  3. IE 는 Foo 도구 막대의 SetSite 메서드 호출
  4. 업데이트 된 컨텐츠를 확인하기 위해, Foo 도구 막대는 https://foo.example.com 에 액세스
  5. Foo 도구 막대는 IE 에 제어 반환
  6. IE 는 초기화를 계속하여 사용자 홈 페이지 표시

문제가 이해 되셨나요?  단계 4를 생각해 주세요. 만약, Foo 도구 막대가 업데이트해야 할 컨텐츠를 다수 찾아내거나, 컨텐츠 서버와의 접속이 늦어지거나 또는 사용자가 오프라인으로 작업하면 어떻게 될까요? 대답은 (애드온은 UI 스레드의 문맥내에서 실행되므로 ) 도구 막대를 위해서, IE 가 장시간 반응하지 않게 되거나 실행과 종료의 소요 시간이 크게 늘어납니다. 보다 좋은 방법은 작업 스레드를 생성하여, 컨텐츠 업데이트를 비동기로 실시하는 것입니다.

추천 방법은 SHCreateThread (영어) (애드온을 C++ 로 개발 한 경우)를 다음과 같이 사용하는 것입니다.

STDMETHODIMP SetSite(IUnknown* pUnkSite)

{

if (pUnkSite != NULL && IsUpdateRequired())

{

        SHCreateThread(Update, NULL, CTF_COINIT | CTF_PROCESS_REF, NULL);

}

else

{

         // Release cached pointers and other resources here.

}

// Return the base class implementation

return IObjectWithSiteImpl<CHelloWorldBHO>::SetSite(pUnkSite);

}

DWORD WINAPI Update(LPVOID pParam)

{

            DWORD dw = 1;

            // Perform update here

           return dw;

}

DWORD WINAPI IsUpdateRequired()

{

           DWORD dw = 1;

            // Perform a low-cost check here to verify that an update should be

            // performed. This can be accomplished by checking a registry key. 

           return dw;

}

앞에서 말한 바와 같이, SetSite 는 신규 스레드를 생성하여 업데이트 메서드를 실행하는 것에 주의해 주세요. 이 방법을 이용하면, SetSite 가 장시간 UI 스레드를 차단할 위험이 없고, 애드온은 컨텐츠를 업데이트할 수 있습니다. 또, 적절한 빈도 (2 , 3 매일등 )로 업데이트 확인을 실시하여, 브라우저나 탭을 열 때마다 업데이트를 확인하는 부담을 사용자에게 주지 않고 애드온을 빠르게 업데이트 할 수 있습니다.

이 방법을 사용하면 장시간 계속되는 처리를 IE 메인 UI 스레드에서 제외할 수 있어 체감상의 성능을 향상시킬 수 있습니다. 다만, 작업 스레드 작업이 반드시 좋은 방법은 아니라는 것은 알고 있어야 합니다. 잠재적인 문제는 여러가지 있습니다. 예를 들어, 작업 스레드에서 이행하는 장점보다, 부하가 높은 스레드에 걸치는 많은 COM 호출 부하가 더 클 가능성이 있습니다.

부스에 도착하고 나서 요금을 지불한다

장시간의 조작을 작업 스레드에서 이행하는 것으로, UI 행을 회피할 수 있습니다. 그럼에도 불구하고, 애드온이 초기화될 때마다, 사용자는 지불할 필요가 없는 비용을 미리 선불해야 할 가능성이 있습니다. 사용자는 IE 를 실행 할 때에 업데이트 컨텐츠를 잘 이용하지 않는 경우가 자주 있습니다. 이러한 경우에서는 사용자와 컨텐츠 공급자 모두가 거기에 알맞은 이익이 없음에도 불구하고, 업데이트 확인에 수반하는 불필요한 비용을 지불합니다.

컨텐츠 업데이트를 실시하는 궁극의 방법은[업데이트 확인]메뉴 항목을 클릭하고, 사용자가 명시적으로 신규 컨텐츠를 요청할 때만 업데이트를 실시하는 방식입니다. 그렇지만, 이 해결책은 애드온 성능을 떨어트릴 가능성이 있기 때문에 많은 경우에 비현실적인 방법입니다. 예를 들어, 드롭 다운 메뉴를 클릭했을 때, 업데이트 컨텐츠가 다운로드되는 동안, 부수된 드롭 다운 메뉴의 표시를 잠깐이라도 기다려야 한다면, 참기 어려울 것입니다. 

더 효과적인 방법으로 사용자 경험과 선불 비용의 균형을 맞추는 기술은 그 밖에 여러 가지 있습니다. 예를 들어, 도구 막대 개발자는 업데이트 확인을 SetSite에서 완전히 이동하여, 사용자가 처음 마우스를 도구 막대 위에 움직였을 때 또는 고정 스케줄에서 업데이트  확인하는 것이 좋을 수도 있습니다. 최적의 해결책은 애드온 마다 다르기 때문에 항상 크리에이티브한 것, 그리고, 가능한 사용자에게 고정된 부담을 주지 않도록 노력하는 것이 중요합니다.

거의 모든 경우에 SetSite 또는 OnDocumentComplete 처리기에서  많은 작업을 회피할 수 있는 방법이 있습니다. 이러한 영역에서 문제를 해결하고, 여분의 시간을 할애하는 것은 성능 문제를 회피하고, 사용자가 확실하게 애드온을 설치할 수 있는 아주 좋은 방법입니다.

레지스트리 사용
  • 원칙 3 : 캐싱 활용
  • 원칙 4 : 나쁜 버릇을 멈춘다 (플러시 금지 )
캐싱을 활용한다

레지스트리 사용에서 연상되는 것은 1996 년 즈음에 유행했던 마카레나 (영어)입니다. 단계를 알고 있는 사람은 극히 소수이지만, 춤을 잘 출 수 있는 사람은 훨씬 더 적었지만, 모두가 참여하는데  그 사실이 전혀 방해되지 않았습니다. 레지스트리의 과도한 사용은 Windows 응용 프로그램에서는 일반적이고, IE8에서는 레지스트리 액세스를 줄이기 위해서 상당한 노력을 했습니다.

레지스트리의 과도한 사용이 꺼려지는 것은 레지스트리 조작의 오버헤드가 중대한 문제가 될 가능성이 있기 때문입니다. 캐시 키의 개폐나 로드는 몇만 주기 분의 부하가 될 수 도 있습니다. 시작시에 각각의 애드온이 수백, 수천, 때로는 수만번 레지스트리에 액세스 하는 것은 비교적 보통이므로, 액세스 수가 누적하면 브라우저가 급속하게 늦어집니다.

다행히, 레지스트리 사용의 부하를 줄이는 것은 가능합니다. 무엇보다도 우선, 공통의 경우에서  최적화합니다. 애드온 실행중, 대부분의 레지스트리 값은 변경되지 않기 때문에 값을 한 번 로드하여 캐시에서 유지하면 각 레지스트리에의 액세스 회수를 크게 줄일 수 있습니다.

레지스트리에의 액세스는 배제할 수 없지만, 나머지 조작의 부하는 줄일 수 있습니다. 레지스트리의 완전 경로 (HKEY_LOCAL_MACHINE\Foo\Bar 등 )를 사용하여 키에 액세스 하는 것은 주어진 루트에서 대상 키까지의 계층수에 의해, 상대경로에 비해 2 , 3 배의 부하가 걸립니다. 애드온에서는 일반적으로, 설정 대부분이 키나 소수의 키 집합의 아래에 있습니다. 예를 들어, 애드온에서 IE 에서 사용되는 연결을 취득해야 합니다. 이 경우, 다음의 레지스트리키에 액세스해야 합니다 (HKEY_LOCAL_MACHINE 아래 ) .

\SOFTWARE\Microsoft\Internet Explorer\Capabilities\FileAssociations

\SOFTWARE\Microsoft\Internet Explorer\Capabilities\MIMEAssociations

\SOFTWARE\Microsoft\Internet Explorer\Capabilities\UrlAssociations

Win32 메서드 RegOpenKey 를 사용하면, 다음에 발췌한 것 같은 코드로 각 regkeys 에 액세스 할 수 있습니다 (예로서 FileAssociations 를 사용합니다) .

HKEY hk;

RegOpenKey(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Internet Explorer\\Capabilities\\FileAssociations", &hk);

나머지 키는 HKEY_LOCAL_MACHINE 를 루트로 하여 같이 액세스 할 수 있습니다. 그렇지만, 이 경우 더 좋은 방법은 Capabilities 키의 핸들을 생성하여, 추가의 상대경로 RegOpenKey 조작을 실시해서, 나머지 값을 취득하는 것입니다. 그 방법은 다음과 같습니다 (다시, 예로서 FileAssociations 를 사용합니다 ) .

HKEY hkRoot;

RegOpenKey(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Internet Explorer\\Capabilities", &hkRoot);

HKEY hkFileAssoc;

RegOpenKey(hkRoot, L"FileAssociations", &hkFileAssoc);

나쁜 버릇을 고치다(플러시 금지)

마지막으로, 레지스트리 값이 확실히 디스크에 써지도록,RegFlushKey 를 사용하는 애드온이 과거에 있었습니다. 상황에 따라서는 다른 탭이나 윈도우에서 실행중의 애드온 두가지의 인스턴스 간의 상태를 유지하기 위해서 이 처리를 실시합니다.

RegFlushKey 에 관한 MSDN 문서 (영어)에서  말한 것처럼, 이 API 를 사용할 필요는 거의 없습니다. 또한 레지스트리를 백업하는 모든 데이터가 확실히 디스크에 쓰여지도록 하기 위해,RegFlushKey 호출은 놀라울 정도로 부하가 높아질 가능성이 있습니다. 이 처리는 호출한 프로그램에 제어가 반환될 때까지 수백 밀리 초가 소요될 가능성이 있습니다.  그리고, 단점은 그 처리가 완료할 때까지, 레지스트리 액세스가 차단됩니다.

결과적으로,RegFlushKey 호출은 IE 에 영향을 줄 뿐만 아니라, 시스템 전체의 성능을 저하시킬 가능성이 있습니다. 인스턴스간의 동기에게 이 레지스트리를 사용하는 애드온은 레지스트리를 플러시하는 것보다도 상태 유지에 RegNotifyChangeKeyValue (영어)를 사용할 수 있습니다. 자세한 것은 다음에 있는 랠리·오스타만과 레이몬드·첸의 블로그 글을 참조해 주세요. 레지스트리 사용에 관한 것으로, 도움이 될 것 입니다. 

애드온 성능 개선에 관한 말씀드린 내용이 지금까지 접한 공통 문제의 몇가지를 이해하는데 도움이 되기를 바랍니다.

Internet Explorer 의 에코시스템에 훌륭한 애드온을 제공해주어 감사합니다. 여러분의 피드백을 기다리겠습니다. 

Christian Stockwell
프로그램 매니저
성능 담당

편집 : 다음의 행에 Root 를 추가 : RegOpenKey(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Internet Explorer\\Capabilities", &hkRoot);

 

*  이 글은 Internet Explorer 개발 팀 블로그 (영어)의 번역 문서입니다. 이 글에 포함된 정보는 Internet Explorer 개발 팀 블로그 (영어)가 생성된 시점의 내용으로, 제품의 사양이나 기능이 보장되는 것은 아닙니다. 이 글에 포함된 정보의 이용은 사용 조건을 참조해 주세요. 그리고, 이 글의 게재 시점에서 Internet Explorer 개발 팀 블로그 (영어)의 내용이 변경될 수 도 있습니다. 최신 정보는 Internet Explorer 개발 팀 블로그 (영어)를 참조하십시오. 

영문 원본 :Designing for Add-on Performance

 

업데이트 일: 2008 년 4 월 8 일