ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • PHP의 Fiber
    분석과탐구 2024. 4. 11. 04:14

    개요

    PHP는 멀티스레딩 지원이 빈약하다. pthread 같은 extension을 잠시 지원했으나 더 이상 지원을 하지 않는다.

    https://www.php.net/manual/en/intro.pthreads.php

    한편, parallel란 익스텐션이 나와 멀티스레딩을 지원하려는 시도가 있었다. 그리고 활발하게 개발되다가 중단되었다.

    https://github.com/krakjoe/parallel/commits/release

    멀티스레딩 지원이 빈약하고 또 개발 중단이 일어나는 이유를 추측해 본다면, 프로그래머가 그것을 필요로 하는 경우가 적기 때문이다. 즉, 수요가 없는 것이다. PHP는 C++, Java같은 일반적인 언어가 아니라 웹 페이지 전달을 목적으로 하기 때문이다. 일반적인 언어에선 처리 속도의 향상, 처리량의 향상을 이유로 멀티스레딩이 요구된다. PHP에선 그렇진 않다.

     

    그런데도, PHP를 하다 보면 멀티스레드가 있으면 좋겠다 싶은 경우가 있다. 파일 입출력, 다른 서버에 API 호출 등의 IO 관련 일을 할 때, 메인 로직이 완전히 멈추지 않고 다른 일을 처리하고 싶은 것이다. 또는, 오래 걸리는 일을 한 번에 끝내지 않고, 잠깐 했다가 다시 메인 로직 돌아오기를 반복하고 싶은 경우도 있다.

     

    위와 같은 요구를 충족시키기 위한 피처가 하나 나왔으니 Fiber(파이버)이다.

    Fiber란?

    Fiber를 이해하는데 완벽한 이미지는 다음과 같다.

    https://php.watch/versions/8.1/fibers#fiber-suspend

    위 이미지에서 얻을 수 있는 정보는 다음과 같다.

    • Fiber가 멀티스레드를 지원하는 것은 아니다. Main과 Fiber를 왔다 갔다 하는 흐름은 하나다.
    • Main의 로직을 시작했다가, Fiber의 로직으로 넘어갈 수 있다. Fiber에서 잠시 중단하고, 중단 결과를 Main으로 보낼 수 있다. 다시 Main에서 Fiber로 데이터를 넘길 수 있다.
    • 즉, Fiber는 자신의 태스크를 처리하다가 중단하면, 어디서 중단했는지 알고 있으며, Main으로부터 다시 데이터를 받아 처리할 수 있는 것이다.

    JS의 async/await과 뭐가 다를까?

    어떻게 보면, JS의 async/await 구조와 비슷하다. JS는 기본적으로 비동기/논블로킹이다. 그러나, 큰 흐름이 아니라 디테일하게 본다면(아래 코드), 과거의 태스크의 응답에 의존하여 다음 태스크 요청에 이전 태스크의 응답을 보내야 해서 동기이다. 그리고, 그 결과를 기다려야 하므로 블로킹이다. 그래서, Fiber와 유사하게 느껴지는 것이다.

    내 머리

    하지만, JS의 async/await류의 비동기 보다 강력하진 않다. 예를 들어, JS의 Promise.all() 메소드를 보자. 이 메소드 인자로 프로미스의 배열을 받아 모든 프로미스가 settled(fulfilled or rejected 된 상태, pending이 아님)가 되면, Promise.all의 콜백이 호출된다. await과 같이 사용하면, 다음과 같다.

    const res = await Promise.all([p1, p2, p3]);

    이때, 강력한 점은 p1, p2, p3가 비동기/논블로킹으로 실행된다는데 있다. 즉, p1이 완료되기를 기다렸다가 p2를... 이 아니라 p1, p2, p3가 따로 멀티스레드로 실행되고, 그 결과를 받아올 수 있다는 점이다. p1이 10초, p2가 5초, p3가 7초 걸린다면, 빠르면 10초에 결과를 받아올 수 있다.

     

    하지만, PHP의 Fiber는 멀티스레드가 아니라서 p1을 실행하고, 기다리고, p2를 실행하고 기다리고... 해야 한다. 그래서, 효율성이란 점에선 Fiber를 쓰나 안 쓰나 동일하다. 빠르면 10초가 아니라, 빨라도 22초인 것이다.

    효율성은 떨어지지만, 어쨌든 concurrency

    Concurrency는 동시성으로 번역된다. 여러 태스크들이 "특정 기간 동안" 실행될 수 있는 특징인 것이다. 관련된 단어 중에 병렬로 번역되는 parallel이 있다. 이것은 "어느 한순간 동시에" 실행될 수 있는 특징을 가리킨다. 예를 들어, 코어가 1개인데 논리코어가 2개인 CPU는 concurrency를, 코어가 2개이고 논리코어는 각각 1개라면 parallel이라 할 수 있다. 왜 이 두 용어를 꺼냈을까? PHP의 Fiber는 concurrency를 갖지만, parallel은 갖지 않기 때문이다.

     

    그래서, Fiber를 어디에 쓸까? 유용하게 쓰일만한 예를 스스로 떠올려보려고 애써봤지만 실패했다. 다른 사람은 어떻게 쓸까 찾아봤는데, 그중 가장 합리적인 아티클에서 발췌하였다.

    Let’s take a concrete example: Imagine you have two jobs which are implemented as fibers. The first one takes around 60 seconds, the second one only 2. The job scheduler that executes fibers, would realize that the first job needs too long, would suspend it -which means that the entire operation, regardless of what it is, is getting stopped — and continue with the second job. Once the second job is finished, the scheduler takes back the first job and continues where it stopped.  - PHP Fibers: What They Are and Where To Use -

    잡 스케줄러가 있고, 잡이 파이버로 구현되었다고 가정해 보자. 처음에 들어온 잡이 60초이고, 그다음에 들어온 잡이 2초이다. 이때, 처음 60초짜리 긴 잡을 잠시 중단하고, 2초짜리 짧은 잡을 실행할 수 있다면 효율적일 것이다. 그리고 다시 긴 잡을 실행 하려면, 어디서 중단했는지 알아야 한다. Fiber가 이것을 가능하게 한다.

    결론

    정말 아쉽지만 Fiber는 async도 아니고 nonblocking도 아니다. 그래서, 효율성이랑 거리가 멀다. 사실 마지막 예제를 보더라도 과연 어디에 쓸까 고민이 된다. 어플리케이션 레벨에선 쓸 일이 극히 드물어 보인다. 프레임워크를 만들어서 잡 스케줄러를 만들면 쓸 일이 있을까? 고민이 필요하다. 

     

    효율성의 관점에선 장점이 없다. 그렇다면, 어떤 특징이 남았지? 중단된 후 상태를 기억한다. 재개할 때, 새로운 데이터를 받아들여서 처리할 수 있다. 이 두 가지를 고려한다면, 서로 다른 로직을 명시적으로 구별하는데 도움이 될 것 같긴한데...

     

    추가....

    참고

    '분석과탐구' 카테고리의 다른 글

    Postman CLI기반 API 테스트 자동화  (1) 2024.04.09
    스트리밍 서비스가 궁금하다  (0) 2023.12.16
    클라우드 트래픽 비용 비교  (0) 2023.11.29
    객체지향 프로그래밍  (0) 2023.10.22
    JWT와 세션  (0) 2023.07.22

    댓글

Designed by Tistory.