Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AsyncClient #88

Merged
merged 5 commits into from
Feb 6, 2025
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
benchmark: add AsyncClient
deedy5 committed Feb 6, 2025
commit 539044427a1eb22336e9573274c353dd3cdcd9f9
47 changes: 44 additions & 3 deletions benchmark/benchmark.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import asyncio
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from io import BytesIO
@@ -46,6 +47,11 @@ def text(self):
("pycurl", PycurlSession),
("primp", primp.Client),
]
AsyncPACKAGES = [
("httpx", httpx.AsyncClient),
("curl_cffi", curl_cffi.requests.AsyncSession),
("primp", primp.AsyncClient),
]


def add_package_version(packages):
@@ -72,9 +78,21 @@ def session_get_test(session_class, requests_number):
s.close()


async def async_session_get_test(session_class, requests_number):
async def aget(s, url):
resp = await s.get(url)
return resp.text

async with session_class() as s:
tasks = [aget(s, url) for _ in range(requests_number)]
await asyncio.gather(*tasks)


PACKAGES = add_package_version(PACKAGES)
AsyncPACKAGES = add_package_version(AsyncPACKAGES)
requests_number = 400

requests_number = 2000
# Sync
for session in [False, True]:
for response_size in ["5k", "50k", "200k"]:
url = f"http://127.0.0.1:8000/{response_size}"
@@ -99,6 +117,30 @@ def session_get_test(session_class, requests_number):
)
print(f" name: {name:<30} time: {dur} cpu_time: {cpu_dur}")

# Async
for response_size in ["5k", "50k", "200k"]:
url = f"http://127.0.0.1:8000/{response_size}"
print(f"\nThreads=1, session=Async, {response_size=}, {requests_number=}")

for name, session_class in AsyncPACKAGES:
start = time.perf_counter()
cpu_start = time.process_time()
asyncio.run(async_session_get_test(session_class, requests_number))
dur = round(time.perf_counter() - start, 2)
cpu_dur = round(time.process_time() - cpu_start, 2)

results.append(
{
"name": name,
"session": "Async",
"size": response_size,
"time": dur,
"cpu_time": cpu_dur,
}
)

print(f" name: {name:<30} time: {dur} cpu_time: {cpu_dur}")

df = pd.DataFrame(results)
pivot_df = df.pivot_table(
index=["name", "session"],
@@ -114,7 +156,7 @@ def session_get_test(session_class, requests_number):
]
print(pivot_df)

for session in [False, True]:
for session in [False, True, "Async"]:
session_df = pivot_df[pivot_df["session"] == session]
print(f"\nThreads=1 {session=}:")
print(session_df.to_string(index=False))
@@ -123,7 +165,6 @@ def session_get_test(session_class, requests_number):
########################################################
# Not for generating image, just to check multithreading working
# Multiple threads
requests_number = 2000
threads_numbers = [5, 32]
for threads_number in threads_numbers:
for response_size in ["5k", "50k", "200k"]:
93 changes: 59 additions & 34 deletions benchmark/generate_image.py
Original file line number Diff line number Diff line change
@@ -2,11 +2,12 @@
import numpy as np
import csv


# Function to read and plot data from a CSV file
def plot_data(file_name, ax, offset):
with open(file_name, 'r') as file:
with open(file_name, "r") as file:
reader = csv.reader(file)
next(reader) # Skip the header row
next(reader) # Skip the header row
data = list(reader)

# Extract the data
@@ -19,43 +20,43 @@ def plot_data(file_name, ax, offset):
cpu_time_200k = [float(row[2]) for row in data]

# Prepare the data for plotting
x = np.arange(len(names)) + offset # the label locations with offset
width = 0.125 # the width of the bars
x = np.arange(len(names)) + offset # the label locations with offset
width = 0.125 # the width of the bars

# Plot Time for 5k requests
rects = ax.bar(x, time_5k, width, label='Time 5k')
ax.bar_label(rects, padding=3, fontsize=7, rotation=90)
rects = ax.bar(x, time_5k, width, label="Time 5k")
ax.bar_label(rects, padding=3, fontsize=7, rotation=90)

# Plot Time for 50k requests
rects = ax.bar(x + width, time_50k, width, label='Time 50k')
ax.bar_label(rects, padding=3, fontsize=7, rotation=90)
rects = ax.bar(x + width, time_50k, width, label="Time 50k")
ax.bar_label(rects, padding=3, fontsize=7, rotation=90)

# Plot Time for 200k requests
rects = ax.bar(x + 2*width, time_200k, width, label='Time 200k')
ax.bar_label(rects, padding=3, fontsize=7, rotation=90)
rects = ax.bar(x + 2 * width, time_200k, width, label="Time 200k")
ax.bar_label(rects, padding=3, fontsize=7, rotation=90)

# Plot CPU time for 5k requests
rects = ax.bar(x + 3*width, cpu_time_5k, width, label='CPU Time 5k')
ax.bar_label(rects, padding=3, fontsize=7, rotation=90)
rects = ax.bar(x + 3 * width, cpu_time_5k, width, label="CPU Time 5k")
ax.bar_label(rects, padding=3, fontsize=7, rotation=90)

# Plot CPU time for 50k requests
rects = ax.bar(x + 4*width, cpu_time_50k, width, label='CPU Time 50k')
ax.bar_label(rects, padding=3, fontsize=7, rotation=90)
rects = ax.bar(x + 4 * width, cpu_time_50k, width, label="CPU Time 50k")
ax.bar_label(rects, padding=3, fontsize=7, rotation=90)

# Plot CPU time for 200k requests
rects = ax.bar(x + 5*width, cpu_time_200k, width, label='CPU Time 200k')
ax.bar_label(rects, padding=3, fontsize=7, rotation=90)
rects = ax.bar(x + 5 * width, cpu_time_200k, width, label="CPU Time 200k")
ax.bar_label(rects, padding=3, fontsize=7, rotation=90)

return x, width, names

# Create a figure with two subplots
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 5), layout='constrained')

# Plot data for 1 thread in the first subplot
x1, width, names = plot_data('session=False.csv', ax1, 0)
# Create a figure with three subplots
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(10, 7), layout="constrained")

x1, width, names = plot_data("session=False.csv", ax1, 0)
x2, _, _ = plot_data("session=True.csv", ax2, 0)
x3, _, x3names = plot_data("session='Async'.csv", ax3, 0)

# Plot data for 8 threads in the second subplot
x2, _, _ = plot_data('session=True.csv', ax2, 0)

# Adjust the y-axis limits for the first subplot
y_min, y_max = ax1.get_ylim()
@@ -67,20 +68,44 @@ def plot_data(file_name, ax, offset):
new_y_max = y_max + 2
ax2.set_ylim(y_min, new_y_max)

# Adjust the y-axis limits for the third subplot
y_min, y_max = ax3.get_ylim()
new_y_max = y_max + 2
ax3.set_ylim(y_min, new_y_max)

# Add some text for labels, title and custom x-axis tick labels, etc.
ax1.set_ylabel('Time (s)')
ax1.set_title('Benchmark get(url).text | Session=False | Requests: 2000 | Response: gzip, utf-8, size 5Kb,50Kb,200Kb')
ax1.set_xticks(x1 + 3*width - width/2) # Adjust the x-ticks to be after the 3rd bar, moved 0.5 bar width to the left
ax1.set_ylabel("Time (s)")
ax1.set_title(
"Benchmark get(url).text | Session=False | Requests: 400 | Response: gzip, utf-8, size 5Kb,50Kb,200Kb"
)
ax1.set_xticks(
x1 + 3 * width - width / 2
) # Adjust the x-ticks to be after the 3rd bar, moved 0.5 bar width to the left
ax1.set_xticklabels(names)
ax1.legend(loc='upper left', ncols=6, prop={'size': 8})
ax1.tick_params(axis='x', labelsize=8)

ax2.set_ylabel('Time (s)')
ax2.set_title('Benchmark get(url).text | Session=True | Requests: 2000 | Response: gzip, utf-8, size 5Kb,50Kb,200Kb')
ax2.set_xticks(x2 + 3*width - width/2) # Adjust the x-ticks to be after the 3rd bar, moved 0.5 bar width to the left
ax1.legend(loc="upper left", ncols=6, prop={"size": 8})
ax1.tick_params(axis="x", labelsize=8)

ax2.set_ylabel("Time (s)")
ax2.set_title(
"Benchmark get(url).text | Session=True | Requests: 400 | Response: gzip, utf-8, size 5Kb,50Kb,200Kb"
)
ax2.set_xticks(
x2 + 3 * width - width / 2
) # Adjust the x-ticks to be after the 3rd bar, moved 0.5 bar width to the left
ax2.set_xticklabels(names)
ax2.legend(loc='upper left', ncols=6, prop={'size': 8})
ax2.tick_params(axis='x', labelsize=8)
ax2.legend(loc="upper left", ncols=6, prop={"size": 8})
ax2.tick_params(axis="x", labelsize=8)

ax3.set_ylabel("Time (s)")
ax3.set_title(
"Benchmark get(url).text | Session=Async | Requests: 400 | Response: gzip, utf-8, size 5Kb,50Kb,200Kb"
)
ax3.set_xticks(
x3 + 3 * width - width / 2
) # Adjust the x-ticks to be after the 3rd bar, moved 0.5 bar width to the left
ax3.set_xticklabels(x3names)
ax3.legend(loc="upper left", ncols=6, prop={"size": 8})
ax3.tick_params(axis="x", labelsize=8)

# Save the plot to a file
plt.savefig('benchmark.jpg', format='jpg', dpi=80, bbox_inches='tight')
plt.savefig("benchmark.jpg", format="jpg", dpi=80, bbox_inches="tight")