จากบทความก่อนหน้านี้
เรามาทำให้ FastAPI ของเราให้เร็วขึ้นถึงเกือบเท่า Go Gin ในบทความนี้ จะทำการใช้ Docker เข้ามาช่วยในการทดสอบความเร็วในการประมวลผล ระหว่าง Python3.11 pypy3.10 และ Go1.20.6 นะครับ
โดย Docker จะถึงตั้งค่าไว้แบบเดี่ยวกัน และใช้ โค๊ต การทำงานแบบเดียวกัน
โค๊ตที่ใช้ในการทดสอบด้วย FastAPI
import timeit
import platform
from fastapi import FastAPI
def make_tree(d):
if d > 0:
d -= 1
return (make_tree(d), make_tree(d))
return (None, None)
def check_tree(node):
(l, r) = node
return 1 if l is None else 1 + check_tree(l) + check_tree(r)
def make_check(d, make=make_tree, check=check_tree):
return check(make(d))
def main(n, min_depth=4):
max_depth = max(min_depth + 2, n)
stretch_depth = max_depth + 1
print('stretch tree of depth {0}\t check: {1}'.format(
stretch_depth, make_check(stretch_depth)))
long_lived_tree = make_tree(max_depth)
mmd = max_depth + min_depth
for d in range(min_depth, stretch_depth, 2):
i = 2 ** (mmd - d)
cs = sum(make_check(d) for _ in range(i))
print('{0}\t trees of depth {1}\t check: {2}'.format(i, d, cs))
print('long lived tree of depth {0}\t check: {1}'.format(
max_depth, check_tree(long_lived_tree)))
app = FastAPI()
@app.get("/")
async def Home():
starttime = timeit.default_timer()
n = 18
d = 4
print("Tree recursion ==> ",n)
print("Python Version ==> ",platform.python_version())
main(n,d)
print("The time difference is :", timeit.default_timer() - starttime," seconds")
return {}
โค๊ตที่ใช้ในการทดสอบด้วย Go gin
package main
import (
"fmt"
"math"
"net/http"
"runtime"
"strings"
"time"
"github.com/gin-gonic/gin"
)
type node struct {
left, right *node
}
func makeTree(d int) *node {
if d > 0 {
d--
return &node{left: makeTree(d), right: makeTree(d)}
}
return nil
}
func checkTree(node *node) int {
if node == nil {
return 0
}
return 1 + checkTree(node.left) + checkTree(node.right)
}
func makeCheck(d int) int {
return checkTree(makeTree(d))
}
func run(n, minDepth int) {
maxDepth := int(math.Max(float64(minDepth+2), float64(n)))
stretchDepth := maxDepth + 1
fmt.Printf("stretch tree of depth %d\t check: %d\n", stretchDepth, makeCheck(stretchDepth))
longLivedTree := makeTree(maxDepth)
mmd := maxDepth + minDepth
for d := minDepth; d <= maxDepth; d += 2 {
i := int(math.Pow(2, float64(mmd-d)))
cs := 0
for j := 0; j < i; j++ {
cs += makeCheck(d)
}
fmt.Printf("%d\t trees of depth %d\t check: %d\n", i, d, cs)
}
fmt.Printf("long lived tree of depth %d\t check: %d\n", maxDepth, checkTree(longLivedTree))
}
func homeHandler(c *gin.Context) {
startTime := time.Now()
n := 18
d := 4
fmt.Println("Tree recursion ==> ", n)
fmt.Println("Go Version ==> ", runtime.Version())
run(n, d)
fmt.Println("The time difference is :", time.Since(startTime), " seconds")
c.JSON(http.StatusOK, gin.H{})
}
func getRealIP(c *gin.Context) string {
// Check X-Real-IP first, then X-Forwarded-For.
// X-Forwarded-For may return multiple IP addresses, and the original client IP will be the first one.
xRealIP := c.Request.Header.Get("X-Real-IP")
xForwardedFor := c.Request.Header.Get("X-Forwarded-For")
if xRealIP != "" {
return xRealIP
} else if xForwardedFor != "" {
return strings.Split(xForwardedFor, ",")[0]
}
return c.Request.RemoteAddr
}
func main() {
r := gin.Default()
r.GET("/", homeHandler)
if err := r.Run(":8080"); err != nil {
fmt.Println("Error starting server:", err)
}
}
docker-compose.yaml
version: "3.9"
services:
pypy-test:
ports:
- 8000:8000
build:
context: .
dockerfile: Dockerfile-pypy
python-test:
ports:
- 8001:8000
build:
context: .
dockerfile: Dockerfile-python
golang-test:
ports:
- 8002:8080
build:
context: .
dockerfile: Dockerfile-golang
Dockerfile-python == Python3.11
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN python -m pip install --no-cache-dir -r requirements.txt
EXPOSE 8000
CMD ["python", "-m","uvicorn", "app2:app","--host", "0.0.0.0","--port","8000"]
Dockerfile-pypy == pypy3.10
FROM pypy:3.10-slim
WORKDIR /app
COPY . .
RUN pypy -m pip install --no-cache-dir -r requirements.txt
EXPOSE 8000
CMD ["pypy","-m","uvicorn", "app2:app","--host", "0.0.0.0","--port","8000"]
Dockerfile-golang == go1.20.6
FROM golang:1.20-alpine
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY app.go .
RUN go build app.go
EXPOSE 8080
CMD ["./app"]
Docker Golang (0.9 วินาที)
Docker pypy3.10 (1.3 วินาที)
Docker python3.11 (6.4 วินาที)
จะเห็นได้ว่าเมื่อใช้งานผ่าน Docker แล้ว Golang 1.20.6 เร็วกว่า pypy3.10 นิดหน่อย เพียง 300-400ms เท่านั้น ในทาง API แล้วถือว่าเป็นทางเลือกที่ดีเลยทีเดี่ยวสำหรับคนที่เขียน Python ด้วย FastAPI