2023-09-02 01:59:17 +00:00
|
|
|
import json
|
2023-10-31 10:02:04 +00:00
|
|
|
from argparse import ArgumentParser
|
|
|
|
from io import BytesIO
|
2023-09-02 01:59:17 +00:00
|
|
|
from typing import Any
|
2023-06-25 18:20:45 +00:00
|
|
|
|
|
|
|
from locust import HttpUser, events, task
|
2023-09-02 01:59:17 +00:00
|
|
|
from locust.env import Environment
|
2023-06-25 18:20:45 +00:00
|
|
|
from PIL import Image
|
2023-10-31 10:02:04 +00:00
|
|
|
|
2023-09-02 01:59:17 +00:00
|
|
|
byte_image = BytesIO()
|
|
|
|
|
|
|
|
|
|
|
|
@events.init_command_line_parser.add_listener
|
|
|
|
def _(parser: ArgumentParser) -> None:
|
|
|
|
parser.add_argument("--clip-model", type=str, default="ViT-B-32::openai")
|
|
|
|
parser.add_argument("--face-model", type=str, default="buffalo_l")
|
2023-10-31 10:02:04 +00:00
|
|
|
parser.add_argument(
|
|
|
|
"--tag-min-score",
|
|
|
|
type=int,
|
|
|
|
default=0.0,
|
|
|
|
help="Returns all tags at or above this score. The default returns all tags.",
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
"--face-min-score",
|
|
|
|
type=int,
|
|
|
|
default=0.034,
|
|
|
|
help=(
|
|
|
|
"Returns all faces at or above this score. The default returns 1 face per request; "
|
|
|
|
"setting this to 0 blows up the number of faces to the thousands."
|
|
|
|
),
|
|
|
|
)
|
2023-09-02 01:59:17 +00:00
|
|
|
parser.add_argument("--image-size", type=int, default=1000)
|
2023-06-25 18:20:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
@events.test_start.add_listener
|
2023-09-02 01:59:17 +00:00
|
|
|
def on_test_start(environment: Environment, **kwargs: Any) -> None:
|
2023-06-25 18:20:45 +00:00
|
|
|
global byte_image
|
2023-09-02 01:59:17 +00:00
|
|
|
assert environment.parsed_options is not None
|
|
|
|
image = Image.new("RGB", (environment.parsed_options.image_size, environment.parsed_options.image_size))
|
2023-06-25 18:20:45 +00:00
|
|
|
image.save(byte_image, format="jpeg")
|
|
|
|
|
|
|
|
|
|
|
|
class InferenceLoadTest(HttpUser):
|
|
|
|
abstract: bool = True
|
|
|
|
host = "http://127.0.0.1:3003"
|
|
|
|
data: bytes
|
|
|
|
|
|
|
|
# re-use the image across all instances in a process
|
2023-09-02 01:59:17 +00:00
|
|
|
def on_start(self) -> None:
|
2023-06-25 18:20:45 +00:00
|
|
|
self.data = byte_image.getvalue()
|
|
|
|
|
|
|
|
|
2023-09-02 01:59:17 +00:00
|
|
|
class CLIPTextFormDataLoadTest(InferenceLoadTest):
|
2023-06-25 18:20:45 +00:00
|
|
|
@task
|
2023-09-02 01:59:17 +00:00
|
|
|
def encode_text(self) -> None:
|
2024-06-07 03:09:47 +00:00
|
|
|
request = {"clip": {"textual": {"modelName": self.environment.parsed_options.clip_model}}}
|
|
|
|
data = [("entries", json.dumps(request)), ("text", "test search query")]
|
2023-09-02 01:59:17 +00:00
|
|
|
self.client.post("/predict", data=data)
|
2023-06-25 18:20:45 +00:00
|
|
|
|
|
|
|
|
2023-09-02 01:59:17 +00:00
|
|
|
class CLIPVisionFormDataLoadTest(InferenceLoadTest):
|
2023-06-25 18:20:45 +00:00
|
|
|
@task
|
2023-09-02 01:59:17 +00:00
|
|
|
def encode_image(self) -> None:
|
2024-06-07 03:09:47 +00:00
|
|
|
request = {"clip": {"visual": {"modelName": self.environment.parsed_options.clip_model, "options": {}}}}
|
|
|
|
data = [("entries", json.dumps(request))]
|
2023-09-02 01:59:17 +00:00
|
|
|
files = {"image": self.data}
|
|
|
|
self.client.post("/predict", data=data, files=files)
|
2023-06-25 18:20:45 +00:00
|
|
|
|
|
|
|
|
2023-09-02 01:59:17 +00:00
|
|
|
class RecognitionFormDataLoadTest(InferenceLoadTest):
|
2023-06-25 18:20:45 +00:00
|
|
|
@task
|
2023-09-02 01:59:17 +00:00
|
|
|
def recognize(self) -> None:
|
2024-06-07 03:09:47 +00:00
|
|
|
request = {
|
|
|
|
"facial-recognition": {
|
|
|
|
"recognition": {
|
|
|
|
"modelName": self.environment.parsed_options.face_model,
|
|
|
|
"options": {"minScore": self.environment.parsed_options.face_min_score},
|
|
|
|
},
|
|
|
|
"detection": {
|
|
|
|
"modelName": self.environment.parsed_options.face_model,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
data = [("entries", json.dumps(request))]
|
2023-09-02 01:59:17 +00:00
|
|
|
files = {"image": self.data}
|
2023-10-31 10:02:04 +00:00
|
|
|
|
2023-09-02 01:59:17 +00:00
|
|
|
self.client.post("/predict", data=data, files=files)
|