☰ 분류

프로파일 결과 읽고 최적화 순서 잡는 프롬프트

측정 결과에서 실제로 시간을 먹는 곳을 찾습니다. 추측으로 고치지 않게 합니다.

분류개발 › 코딩
태그분석코드개발자
프롬프트 (영어 본문 · 답은 한국어로 옵니다)
Read this profile and plan the optimization.

Produce:
1. Where time actually goes, as percentages of the total. *Reason only from the measurements. Do not rank by what looks inefficient in the code.*
2. For each of the top three costs: **the end-to-end gain if it were eliminated entirely.** A function taking 8% of the time cannot make the request more than 8% faster — this ceiling decides whether it is worth touching.
3. Distinguish self time from cumulative time. Reading these the wrong way round is the most common misreading of a profile.
4. Whether the cost is algorithmic, I/O bound, allocation, contention, or startup. Each has a different fix and only one of them is helped by faster code.
5. **Calls that should not be happening at all** — repeated work, N+1 patterns, work done in a loop that could be hoisted. Removing a call beats optimizing it.
6. Ordered plan: effort versus gain, with the target in view. Say where to stop.

Rules:
- *If the profile does not explain the latency I am seeing, say so* and name what to measure next — the bottleneck may be outside what was profiled.
- Say when the answer is "this is already fast enough, do not optimize".
- Note anything that would make the code harder to maintain for a gain below a few percent, and advise against it.
붙여 넣으면 맨 아래에 채울 칸(프로파일 결과 · 관련 코드 · 목표 성능)이 나옵니다

어떤 프롬프트인가

최적화는 대개 엉뚱한 데를 고치다 끝난다. 이 프롬프트는 측정값에서만 판단하고, 고쳤을 때 전체가 얼마나 빨라지는지를 먼저 계산해 할 가치가 있는지부터 가린다.

모델 비교

ChatGPT가 불확실성을 가장 잘 통제해 우수하다. Gemini는 구체적이나 프로파일로 입증되지 않은 귀속·예측이 많고, [C]는 답이 없다.

ChatGPT종합 1위
43/ 50

+ self·누적 시간을 신중하게 구분했다.

- 잔여 시간 계산이 틀리고 중첩 설명이 모호하다.

Gemini
36/ 50

+ 측정값별 상한과 실행 순서를 구체화했다.

- 근거 없이 잔여 비용과 20~50ms를 단정했다.

기준ChatGPTGemini앞선 모델
지시 준수98ChatGPT +13%
정확성85ChatGPT +60%
구체성99동점
구성·가독성98ChatGPT +13%
분량 적정성86ChatGPT +33%

gpt-5.6-sol가 모델 이름을 가린 채 1~10점으로 채점했습니다 (2026-09-24). 측정값이 아니라 AI 심사 결과입니다.

답 전문 보기

같은 입력을 세 모델에 넣어 받은 답을 그대로 옮겼습니다. 각 CLI(에이전트 하네스)에서 실행했습니다. ChatGPT·Claude 앱이나 웹에서는 답이 다를 수 있습니다.

넣은 입력
프로파일 결과: cProfile 결과 (Django, GET /api/orders/ 요청 1회):

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    12045    3.812    0.000    3.812    0.000 {method 'execute' of 'psycopg2.extensions.cursor' objects}
     8901    2.104    0.000    2.104    0.000 {built-in method _socket.socket.recv}
    45000    1.203    0.000    1.950    0.000 orders/serializers.py:88(get_discount_price)
        1    0.045    0.045    9.812    9.812 orders/views.py:34(OrderListView.list)
    45000    0.611    0.000    0.611    0.000 {method 'strftime' of 'datetime.datetime' objects}
    12045    0.302    0.000    4.114    0.000 orders/models.py:112(Order.get_items)

Total time: 9.812s, HTTP 200

관련 코드: # orders/views.py
class OrderListView(APIView):
    def get(self, request):
        orders = Order.objects.filter(store_id=request.user.store_id)
        data = OrderSerializer(orders, many=True).data
        return Response(data)

# orders/serializers.py
class OrderSerializer(serializers.ModelSerializer):
    discount_price = serializers.SerializerMethodField()

    def get_discount_price(self, obj):
        # 쿠폰을 주문 건마다 매번 새로 조회
        coupon = Coupon.objects.filter(store=obj.store, active=True).first()
        return obj.price - (coupon.amount if coupon else 0)

목표 성능: 현재 주문 목록 API 평균 응답 9.8초, 피크 시간대 동시 요청 50건까지는 버텨야 합니다. 300ms 이내로 줄이는 게 목표예요.
gpt-5.6-luna (codex CLI · 에이전트 하네스) · 실행일 2026-09-24

프로파일만 보면 평균 9.8초의 원인은 명확합니다. 다만 목표인 300ms는 단순한 Python 최적화로는 달성할 수 없습니다. DB 호출 수와 반환 데이터 규모를 크게 줄여야 합니다.

1. 시간 분포

Total time = 9.812초 기준입니다. tottime은 해당 함수 자체 시간이며, 자식 호출 시간은 제외합니다.

항목 self time 전체 비율 cumulative time 해석
cursor.execute 3.812초 38.9% 3.812초 SQL 실행 대기
socket.recv 2.104초 21.4% 2.104초 DB/네트워크 응답 수신 대기
get_discount_price 1.203초 12.3% 1.950초 할인 계산 및 내부 호출
strftime 0.611초 6.2% 0.611초 날짜 문자열 변환
Order.get_items 0.302초 3.1% 4.114초 자체 시간은 작지만 하위 SQL 포함
기타 호출/프레임워크 약 1.78초 약 18.1% — 직렬화, Django, 호출 오버헤드 등

get_items의 cumulative 4.114초는 execute 시간을 포함합니다. 따라서 4.114초 + 3.812초처럼 더하면 안 됩니다.

마찬가지로 get_discount_price의 cumulative 1.950초는 내부 호출 시간을 포함하므로, 독립적인 추가 비용은 self time인 1.203초에 가깝습니다.

2. 상위 세 비용과 제거 시 이론상 최대 이득

1위: DB execute

  • self time: 3.812초
  • 전체의 38.9%
  • 전부 제거해도 이론상 최대 약 38.9% 단축
  • 9.812초 → 약 6.000초

이는 알고리즘 문제가 아니라 DB I/O 비용입니다. SQL 개수가 12,045회라는 점이 핵심입니다. 인덱스 하나보다 먼저 쿼리 수를 줄여야 합니다.

2위: socket.recv

  • self time: 2.104초
  • 전체의 21.4%
  • 전부 제거해도 이론상 최대 약 21.4% 단축
  • 9.812초 → 약 7.708초

이 역시 Python 코드가 느린 것이 아니라 응답을 기다리는 I/O입니다. DB 처리 시간, 반환 행 수, 네트워크 왕복 횟수와 관련 있을 가능성이 큽니다.

execute와 recv는 서로 완전히 독립적인 비용이라고 볼 수 없으므로 38.9%와 21.4%를 단순 합산하면 안 됩니다.

3위: get_discount_price

  • self time: 1.203초, 12.3%
  • cumulative time: 1.950초, 19.9%
  • 함수와 하위 작업을 통째로 제거한다는 의미라면 최대 약 19.9%
  • 함수 자체의 Python 비용만 제거한다면 최대 약 12.3%

호출 횟수가 45,000회입니다. 주문마다 쿠폰을 조회하는 코드와 일치하는 전형적인 N+1 패턴입니다. 이 비용은 단순히 함수 내부를 미세 최적화할 대상이 아니라, 호출 구조 자체를 제거해야 하는 대상입니다.

3. 비용의 성격

  • cursor.execute: DB I/O bound
  • socket.recv: 네트워크/DB 응답 I/O bound
  • get_discount_price: 반복 작업 및 N+1 가능성
  • strftime: 반복적인 CPU 변환 비용
  • Order.get_items: cumulative 기준으로는 DB 호출을 포함한 데이터 접근 비용
  • 45,000회 직렬화/날짜 변환: 반복 호출과 객체/문자열 생성 비용

현재 측정으로는 “느린 알고리즘”이 주된 원인이라고 보기 어렵습니다. 빠른 Python 코드로 바꿔도 DB 대기와 네트워크 대기가 남습니다.

4. 발생하지 않아야 할 호출

가장 명확한 문제는 이것입니다.

coupon = Coupon.objects.filter(
    store=obj.store,
    active=True
).first()

이 코드가 주문마다 실행됩니다. 45,000번의 할인 계산 호출이 있으므로, 쿠폰 조회도 주문 수에 비례해 반복될 가능성이 큽니다.

개선 방향은 다음 우선순위입니다.

  1. 매장별 활성 쿠폰을 한 번만 조회합니다.
  2. 주문 목록을 직렬화하기 전에 매장별 쿠폰을 미리 로드합니다.
  3. 가능하면 주문 쿼리와 쿠폰 쿼리를 배치 처리합니다.
  4. 동일한 매장에 대한 쿠폰 조회를 요청 단위 캐시로 재사용합니다.
  5. get_items가 주문마다 추가 쿼리를 만든다면 prefetch_related 또는 집계 쿼리로 바꿉니다.

단, 45,000회라는 수치와 현재 예시 코드의 주문 조회 구조 사이에는 확인이 필요합니다. 실제로는 페이지네이션 없이 매우 많은 주문을 반환하고 있거나, serializer 내부에 추가적인 반복 호출이 있을 가능성이 있습니다.

5. 300ms 목표에 대한 판단

현재 9.812초에서 300ms는 약 97% 단축입니다.

상위 비용 하나를 완전히 제거해도:

  • execute 제거: 약 6.0초
  • socket.recv 제거: 약 7.7초
  • 할인 함수 전체 제거: 약 7.9초

따라서 상위 한두 함수를 최적화하는 정도로는 목표에 도달하지 못합니다. 특히 45,000개 레코드를 한 번에 직렬화하고 있다면, 300ms 목표와 응답 형태 자체가 충돌할 수 있습니다.

필수 조치는 다음과 같습니다.

  • 페이지네이션 도입
  • 반환 필드 축소
  • 주문별 쿼리 제거
  • 쿠폰 조회 배치화 또는 select_related/prefetch_related
  • get_items의 쿼리 수와 SQL 실행 계획 확인
  • 날짜 포맷팅을 반환 행 수에 비례하지 않도록 줄이거나 필요한 필드만 변환
  • DB 인덱스와 실제 SQL 실행 시간 확인

6. 권장 실행 순서

  1. 쿼리 수부터 줄이기 — 가장 높은 우선순위

    12,045회 execute를 목표로 수십 회 이하로 줄입니다. 쿠폰 N+1과 get_items의 추가 쿼리를 먼저 확인합니다.

  2. 페이지네이션과 응답 크기 제한

    45,000회 serializer 호출이 실제 응답 데이터 규모를 의미한다면, 300ms 목표를 위해 한 요청의 처리 건수를 제한해야 합니다.

  3. SQL 실행 계획과 인덱스 확인

    쿼리 수를 줄인 후에도 개별 SQL이 느리면 EXPLAIN ANALYZE, 필터 컬럼 인덱스, 정렬 조건을 확인합니다.

  4. 직렬화 비용 측정 및 축소

    strftime과 할인 계산은 DB 비용을 해결한 뒤 처리합니다. 이 단계의 최댓값은 현재 약 1~2초 수준이므로 단독으로는 목표를 만들 수 없습니다.

  5. 재프로파일링

    SQL 수, 총 DB 시간, 응답 바이트 수, serializer 호출 수를 다시 측정합니다.

현재 프로파일은 서버 내부 요청 처리 시간을 설명합니다. 만약 사용자가 보는 9.8초가 이 값과 다르다면, 다음도 별도로 측정해야 합니다.

  • 웹 서버/프록시 대기 시간
  • DB 서버에서 실제 실행된 총 시간
  • DB 연결 획득 시간
  • 응답 직렬화 후 전송 시간
  • 응답 크기와 클라이언트 수신 시간
  • 동시 요청 50건에서의 DB lock/connection-pool 대기

결론적으로, strftime 미세 최적화나 serializer 메서드의 Python 문법 변경은 권하지 않습니다. 전체의 몇 퍼센트 미만만 줄이고 유지보수성을 떨어뜨릴 가능성이 큽니다. 먼저 N+1 제거, 쿼리 배치화, 페이지네이션을 적용하고, 그 결과가 300ms에 근접했을 때만 남은 비용을 최적화해야 합니다.

같은 분류의 프롬프트

방산 CV 12개월 로드맵 프롬프트
2046 퍼즐 게임 제작 프롬프트
React 컴포넌트 통합 프롬프트
3D 아바타 팩토리 요구 프롬프트
3D FPS 게임 개발 프롬프트