Web
수정·

FastAPI와 NestJS 의 단순 IO 성능 비교

by Dohoon Kim · 22년 08월 22일 09:00:00

FastAPi, NestJS간의 비교

Python의 Web Framework FastAPI 와 Node 의 Web Framework를 DB R/W 를 통한 단순 성능 비교를 해보았다 테스트 방식은 wrk + luascript 를 이용하여 10초간 최대한 많은 data를 Post Method 를 이용하여 생성 요청을 보내고, 최대한 많은 데이터를 GET method 를 통하여 조회하도록 하였다.

공통 설정

  • Database : Postgres 14.3
$ docker run -it --rm \
-e POSTGRES_DB=postgres \
-e POSTGRES_DB_USER=postgres \
-e POSTGRES_DB_PASSWORD=postgres \
--network host \
postgres:latest
  • Test Entity
class Entity : 
    id = Column(Integer,pk=True, autoincrement=True)
    title = Column(String, not_null = True)
    content = Column(String, not_null = True)

FastAPI 설정

  • ORM : SQLAlchemy['asyncio'] 사용
  • JSON Serializer : Pydantic 이용
  • 기타 : 비동기 처리를 위해 asyncpg 를 이용하였음.
  • 실행옵션
$ uvicorn --host 0.0.0.0 --port 8000 --no-access-log --workers=1
# 입력, 조회 코드

@app.get('/{id:int}', response_model = TestEntityScheme)
async def get(id : int, session : AsyncSession = Depends(get_session))  : 
    stmt = select(TestEntity).where(TestEntity.id==id)
    future = await session.execute(stmt)
    data = future.scalar()
    return data

@app.post('/')
async def  post(model : TestScheme, session : AsyncSession = Depends(get_session)) : 
    new_entity = TestEntity(model)
    session.add(new_entity)
    return model

Nestjs 설정

  • ORM : typeorm
  • 기타 : Nestjs 는 backend로 express와 fastify 를 지원하여 둘 다 테스트를 진행하였다.
  • 실행옵션
$ npm run start
// 입력/조회 코드
  async create(form : TestEntityRegisterScheme) : Promise<TestEntityScheme>{
    const gen  =  this.entity.create(form);
    const ret = await this.entity.save(gen)
    if(ret){
      return ret;
    }

    throw new HttpException("fail to create entity.", HttpStatus.CONFLICT);
  }

  async fetch(id : number) : Promise<TestEntityScheme>{ 
    // console.log("find by id : ", id)
    const entity : TestEntityScheme = await this.entity.findOneBy({id:id})
    return entity;
  }

결과

FastAPI + Uvicorn + Asyncpg + SQLAlchemy

$ (benchmark) ➜  wrk git:(master) ✗ ./wrk http://127.0.0.1:8000 -s ./scripts/post.lua
Running 10s test @ http://127.0.0.1:8000
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    31.91ms   70.07ms 435.51ms   89.09%
    Req/Sec     0.95k   401.81     1.34k    77.91%
  16969 requests in 10.01s, 2.75MB read
Requests/sec:   1695.83
Transfer/sec:    281.22KB
$ (benchmark) ➜  wrk git:(master) ✗ ./wrk http://127.0.0.1:8000 -s ./scripts/get.lua
Running 10s test @ http://127.0.0.1:8000
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     9.56ms   12.82ms 148.44ms   93.63%
    Req/Sec   715.47    180.99     0.93k    67.34%
  14184 requests in 10.01s, 2.42MB read
Requests/sec:   1417.18
Transfer/sec:    247.14KB

NestJS + TypeORM + PG + Express

$ (benchmark) ➜  wrk git:(master) ✗ ./wrk http://127.0.0.1:8000 -s ./scripts/post.lua
Running 10s test @ http://127.0.0.1:8000
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     3.00ms    6.64ms 129.87ms   98.54%
    Req/Sec     2.11k   390.20     3.59k    86.57%
  42189 requests in 10.10s, 11.46MB read
Requests/sec:   4176.98
Transfer/sec:      1.13MB
$ (benchmark) ➜  wrk git:(master) ✗ ./wrk http://127.0.0.1:8000 -s ./scripts/get.lua 
Running 10s test @ http://127.0.0.1:8000
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     2.66ms  723.29us   7.92ms   78.88%
    Req/Sec     1.87k   263.51     2.16k    90.51%
  29419 requests in 10.00s, 8.09MB read
  Socket errors: connect 0, read 0, write 0, timeout 10
Requests/sec:   2940.47
Transfer/sec:    827.94KB

NestJS + TypeORM + PG + Fastify

(benchmark) ➜  wrk git:(master) ✗ ./wrk http://127.0.0.1:8000 -s ./scripts/post.lua
Running 10s test @ http://127.0.0.1:8000
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.61ms    6.13ms 124.44ms   98.76%
    Req/Sec     4.89k     1.04k    6.11k    85.57%
  97879 requests in 10.10s, 20.61MB read
Requests/sec:   9692.73
Transfer/sec:      2.04MB
(benchmark) ➜  wrk git:(master) ✗ ./wrk http://127.0.0.1:8000 -s ./scripts/get.lua 
Running 10s test @ http://127.0.0.1:8000
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.25ms  647.62us  20.98ms   96.01%
    Req/Sec     4.11k   490.81     4.65k    89.11%
  82676 requests in 10.10s, 17.70MB read
Requests/sec:   8185.85
Transfer/sec:      1.75MB

결과

단순 IO bound 회피 성능을 확인해보고자 한 테스트였고 그에 따른 성능의 결과는 NestJS-Fastify (초당 8000건) > NestJS-Express(초당 3500건) > FastAPI(초당 1600건) 순으로 나타났다. 프레임워크에 대한 조작 미숙으로 인해 발생한 문제라면 해당하지 않겠지만 FastAPI 가 주장하는 express 급 성능은 단순히 String만을 바로 반환하는 Endpoint 에서만 나타났고 DB 조회 쪽에선 급격히 떨어지는 것으로 보인다. 이 성능이 json serializer 로 인한 성능 차이인지도 확인이 필요하다.