Pydantic을 불필요한 곳에서 가급적 사용하지 말자.

BSH·2023년 11월 5일
1

이 글은 하이퍼 커넥트의 Pydantic은 아주 느리다. 불필요한 곳에서 가급적 사용하지 말자#5글을 참고하여 작성하였습니다.

Pydantic

Pydantic은 FastAPI에서 사용하는 데이터 검증과 parsing 라이브러리입니다.
아래 처럼 사용이 가능합니다.

from datetime import date
from pydantic import BaseModel

class User(BaseModel):
  user_id: int
  birthday: date


User.model_validate_json('{"user_id": "100", "birthday": "2000-01-01"}')

FastAPI는 내부적으로 Pydantic을 포함하고 있고, API request 변수와 response type을 Pydantic 객체로 넣어주면 Swagger UI 까지 자동으로 만들어주고 있습니다. 그만큼 FastAPI를 사용한다면 Pydantic은 필수적으로 알아야합니다.

Pydantic은 정말 편리하지만 data validation이 필요 없는 환경에서도 Pydantic을 남발해서는 안됩니다. 검증 및 parsing에 드는 시간이 많이 소요되기 때문입니다.

Pydantic의 속도를 보기전 하나 짚어야할 점은 Pydantic이 V1에서 V2로 바뀌면서 내부가 Rust구현으로 바뀌고 내장기능도 일부 바뀌었다는 것입니다. 하이퍼 커넥트의 블로그는 1 버전에서 실행한 결과인데 현재 2 버전으로 바뀌었는데 성능차이는 어떨까요?

import timeit
from typing import List

from pydantic import BaseModel

class FeatureSet(BaseModel):
  user_id: int
  features: List[float]

def create_pydantic_instances() -> None:
  for i in range(400):
    obj = FeatureSet(
      user_id=i,
      features=[1.0 * i + j for j in range(50)],
    )

elapsed_time = timeit.timeit(create_pydantic_instances, number=1)
print(f"pydantic: {elapsed_time * 1000:.2f}ms")

# pydantic: 2.22ms

v1에서는 12ms가량 소요됐는데 v2는 2ms밖에 걸리지 않습니다.

import timeit
from typing import List

class FeatureSet:
  def __init__(self, user_id: int, features: List[float]) -> None:
    self.user_id = user_id
    self.features = features
    
def create_class_instances() -> None:
  for i in range(400):
    obj = FeatureSet(
      user_id=i,
      features=[1.0 * i + j for j in range(50)],
    )

elapsed_time = timeit.timeit(create_class_instances, number=1)
print(f"class: {elapsed_time * 1000:.2f}ms")

# class: 1.14ms

그래도 기본 class로 생성하는게 2배정도 빠르네요.

profile
컴공생

0개의 댓글