Javascript 장시간 실행 작업 - CPU 사용 유휴 시간

15669 단어 webdevjavascript
원활한 사용자 환경을 제공하기 위해 브라우저는 초당 60프레임을 렌더링해야 합니다. 이것은 16ms마다 한 프레임을 렌더링한다는 것을 의미합니다.만약 장시간 실행된javascript 작업이 있다면, 사용자가 애니메이션을 스크롤하거나 렌더링하고 있다면, 프레임을 삭제하기 시작할 것입니다.
일부 기술은 사용자 인터페이스를 파괴하는 것을 피할 수 있는데, 그 중에서 가장 흔히 볼 수 있는 것은 이런 임무를 웹 작업자에게 이전하는 것이다.이 글에서 나는 어떻게 일을 블록으로 분할하고 CPU의 여가 시간을 사용하여 그것들을 처리하는지 다른 방법을 연구할 것이다.React팀은 광섬유 구조에서 이런 기술을 사용했다. 트리의 조화를 중단하고 더 우선적인 업무를 하도록 함으로써 사용자의 perceived performance을 향상시킬 수 있다.
주의: 본고의 모든 내용은react광섬유 구조의 계발을 받았다(그러나 매우 간단한 방법을 사용했다).참고 자료 부분으로 넘어가면react의 작업 원리를 이해하는 데 도움을 줄 수 있는 자원을 얻을 수 있습니다.

테스트 용례


100000개의 노드를 포함하는 목록 중 이전 노드의 값에 따라 노드의 값을 계산합니다. 사용자가 첫 번째 노드를 변경할 때 이 체인의 각 노드를 다시 계산하고 9999개의 노드를 생성하여 차단 계산을 실행해야 합니다.
다음 인터페이스가 있는 노드:
interface INode {
    id: string;
    value: number | null;
    previousId: string | null;
    nextId: string | null;
}
노드 맵 만들기:
const nodes = new Map<INode>();
nodes.set('A1', {
  id: 'A1',
  nextId: 'A2',
  previousId: null,
  value: 99
});
nodes.set('A2', {
  id: 'A2',
  nextId: 'A3',
  previousId: 'A1',
  value: null
});

...

nodes.set('A100000', {
  id: 'A100000',
  nextId: null,
  previousId: 'A99999',
  value: null
});

요구 사항


우리의 솔루션은 다음과 같은 요구 사항을 지원해야 합니다.
  • 에 버려진 프레임이 없습니다. 페이지는 항상
  • 에 응답해야 합니다.
  • 처리는 중단될 수 있어야 합니다 (신규 데이터가 도입되었거나 사용자가 페이지를 떠나려고 하기 때문에)
  • 이전의 제약 조건을 고려하여
  • 은 가능한 한 빨리 해야 한다. (만약 우리가 블록을 나누어 진행할 경우 처리 시간이 좀 길지만 페이지가 응답하기 때문에 감지 성능이 더욱 좋아질 것이다)
  • 어떻게 우리 방법의 질을 평가합니까?

  • 간단한 응용 프로그램을 만듭니다. - 저는 반응 응용 프로그램을 만드는 응용 프로그램을 사용할 것입니다.
  • 은 스크롤 가능한 구역과 사용자의 상호작용을 테스트할 수 있는 애니메이션을 추가했다.
  • async-render-toolboxchrome 확장을 사용하여 CPU 지연의 시각적 알림을 얻는다.
  • 은 devtools를 사용하여 추가 성능 검사를 실시한다.
  • 네, 이것은 그리 과학적이지 않습니다...그러나 우리가 진정으로 개선하고자 하는 것은 감지 성능이다. 이것은 일종의 감각적 체험과 같다.

    CPU 사용 유휴 시간


    The window.requestIdleCallback() method queues a function to be called during a browser's idle periods. This enables developers to perform background and low priority work on the main event loop, without impacting latency-critical events such as animation and input response. Functions are generally called in first-in-first-out order; however, callbacks which have a timeout specified may be called out-of-order if necessary in order to run them before the timeout elapses.


    requestIdleCallback을 호출하여 다음 CPU 유휴 기간을 조정합니다.이 리셋에서, 우리는 deadline.timeRemaining()을 호출하여 여가 기간이 끝날 때까지 얼마나 남았는지 검사할 수 있다.유휴 시간의 최대치는 50ms이지만, 대부분의 경우 우리가 얻는 시간은 50ms보다 적다. 이것은 CPU의 바쁜 정도에 달려 있다.
    잉여 시간과 매번 계산된 고정 최대 시간을 사용하면 우리는 여유 시간이 있는지 다시 계산하거나 다음 여유 기간으로 재배치할 수 있다.우리는 더 많은 임무를 집행해야 할 때까지 새로운 소환을 안배할 것이다.이러한 방식으로 노드를 처리하면 관건적인 사건을 중단하지 않고 원활한 사용자 체험을 제공할 수 있습니다.

    일을 안배하다


    CPU의 여유 시간을 사용하고 있기 때문에 사용자는 언제든지 페이지와 상호작용하고 새로운 작업을 배정할 수 있습니다.이것은 우리가 일을 기다리는 대열을 보류해야 한다는 것을 의미한다.
    주어진 노드를 처리하고 이 노드에 새로운 작업을 배정하고 있다면, 우리는 현재 작업을 중단하고 이 노드를 다시 대기열의 끝까지 밀어야 한다.
    interface IUnitOfWork {
        triggerNodeId: string;
        node: INode;
    }
    
    let workQueue: INode[] = [];
    let nextUnitOfWork: IUnitOfWork | null = null;
    
    function scheduleWork(node: INode): void {
        /**
         * Verify if there is already a work being
         * process that was triggered by the same node
         */
        const isInProgress = nextUnitOfWork && nextUnitOfWork.triggerNodeId === node.id;
    
        if (isInProgress) {
            nextUnitOfWork = null;
        }
        workQueue.push(node);
    
        requestIdleCallback(performWork);
    }
    
    우리의 방법은 CPU의 사용 가능한 시간을 바탕으로 하는 것이지만, 사용 가능한 시간이 한 단위의 일을 완성할 수 있다는 것을 어떻게 알 수 있습니까?응, 그건 과자야!현재 이 문제를 해결하는 방법은 우리가 통상적으로 모든 작업 단원을 처리하고 상수 ENOUGH_TIME에 저장하는 데 필요한 중간 시간을 가정하는 것이다.이것은 조정이 필요할 것이다. 이것은 매우 구체적인 작업이 될 것이다. 너는 반드시 응용 프로그램에 있어야 한다.
    const ENOUGH_TIME = 2; // in ms
    
    앞에서 본 바와 같이, 우리가 작업을 계획할 때, 우리는 requestIdleCallback을 호출했고, 이것은 최종적으로 우리의 performWork 함수를 호출할 것이다.이 함수에서 우리는 workLoop을 시작합니다.workLoop에서 다음 작업 단원을 가져옵니다. 없으면 작업 대기열에서 새 노드를 선택하십시오.그리고while 순환에서 performUnitOfWork 함수를 호출하기 시작합니다. 우리가 한 점에 도달할 때까지, 우리는 더 많은 시간이 없거나 더 많은 작업 단원이 없다고 생각합니다.performUnitOfWork은 각 노드를 처리하는 함수입니다. (이 함수는 상세하게 소개하지 않습니다. 이 예에서 주로 가상 계산이기 때문입니다.)workLoop이 완성되면 performLoop 함수로 돌아갑니다. 작업 대기열에 next Unit OfWork나 노드가 남아 있다면, 새로운 빈 리셋을 설정하고 이 과정을 다시 시작합니다.
    function resetNextUnitOfWork() {
        const node = workQueue.shift();
        if (!node) return;
    
        nextUnitOfWork = { triggerNodeId: node.id, node };
    }
    
    function workLoop(deadline: number): void {
        if (!nextUnitOfWork) {
            resetNextUnitOfWork();
        }
    
        while (nextUnitOfWork && deadline.timeRemaining() > ENOUGH_TIME) {
            nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
        }
    }
    
    function performWork(deadline: number): void {
        workLoop(deadline);
    
        if (nextUnitOfWork || workQueue.length > 0) {
            requestIdleCallback(performWork);
        }
    }
    

    결과


    블록 교체 방법의 실행 속도는 훨씬 빠르지만, 다음gif에서 보듯이 프레임을 버리는 경우가 많다.페이지가 한동안 응답하지 않을 것입니다.

    유휴 리셋 방법은 CPU의 사용 빈도에 따라 예측할 수 없는 실행 시간을 필요로 하지만 페이지는 항상 응답성을 가지기 때문에 감지된 성능이 더 좋을 수 있습니다.

    this video을 검사하여 본문을 작성할 때 작성한 예시의 출력 결과를 보십시오.

    결론


    이 단독 테스트에서 Request Idle Callback을 사용하는 방법은 우리의 요구를 검사한 것 같다.
    만약 우리가 100개의 계산을 처리한다면, 여가 실행 시간은 일반적인 차단 조작과 차이가 크지 않지만, 만약 우리가 10만 개의 계산을 처리한다면, 여가 방법은 더욱 긴 시간을 들일 것이지만, 더욱 매끄러울 것이다.이것은 일종의 균형이다. 나 개인적으로 말하자면, 나는 이것이 가치가 있다고 생각한다.
    하지만 주의해야 할 것은 browser support은 아직 이상적이지 않다는 것이다...IE Edge 또는 Safari는 지원되지 않습니다...항상 그 두 개 맞죠?😞 어떤 방법은 그것을 채울 수 있다. 예를 들어 이 간단한 gistreact's approach은 더욱 복잡하고 건장한 방법이다.
    그러나 몇 가지 주제는 더욱 연구해야 한다.
  • 과react의 스케줄러 통합 효과는 어떻습니까?
  • 대부분의 Request Idle Callback에 따르면
  • 패드는 Request Idle Callback이 무엇을 해야 하는지 정확하게 표시할 수 없다.우리는 좋은 패드를 찾을 수 있고, 심지어는 반응으로 사용할 수 있습니까?
  • 이것은 웹workers(또는 다른 가능한 방법)를 사용하는 것보다 어떻습니까나는 앞으로의 문장에서 이 문제에 대답할 수 있기를 바란다.
  • 리소스

  • Github repo with code presented in this article
  • Udacity's "Browser Rendering Optimization" course by Google
  • Perceived Performance
  • Fiber Principles: Contributing To Fiber
  • The how and why on React’s usage of linked list in Fiber to walk the component’s tree
  • Using requestIdleCallback
  • 면책 성명: 의견은 내 것이지 고용주의 의견이 아니다.
    만약 당신이 어떤 잘못을 발견한다면, 나의 엉망진창인 영어든 기술적인 세부 사항이든 부끄러워하지 말고 저에게 트위터를 보내주세요.나는 이 박문을 끊임없이 개선하기 위해 노력할 것이다:simple_미소:

    좋은 웹페이지 즐겨찾기