1111)
1212import math
1313import uuid
14+ import sys
15+ import os
16+ sys .path .append (os .path .abspath (os .path .join (os .path .dirname (__file__ ), '..' )))
17+ from tests .perf .microbenchmarks .resource_monitor import ResourceMonitor
1418
1519async def upload_one_async (bucket_name , object_name , upload_size , chunk_size ):
1620 """Uploads a single object of size `upload_size`, in chunks of `chunk_size`"""
1721 print (f"Uploading { object_name } of size { upload_size } in chunks of { chunk_size } to { bucket_name } from process { os .getpid ()} and thread { threading .get_ident ()} " )
1822 client = AsyncGrpcClient ().grpc_client
1923 start_time = time .perf_counter ()
2024 writer = AsyncAppendableObjectWriter (
21- client = client , bucket_name = bucket_name , object_name = object_name
25+ client = client ,
26+ bucket_name = bucket_name ,
27+ object_name = object_name ,
28+ writer_options = {"FLUSH_INTERVAL_BYTES" : 1026 * 1024 ** 2 },
2229 )
2330
2431 await writer .open ()
@@ -30,7 +37,7 @@ async def upload_one_async(bucket_name, object_name, upload_size, chunk_size):
3037 await writer .append (data )
3138 uploaded_bytes += bytes_to_upload
3239 count += 1
33- await writer .close ()
40+ await writer .close (finalize_on_close = True )
3441 assert uploaded_bytes == upload_size
3542 assert count == math .ceil (upload_size / chunk_size )
3643
@@ -42,54 +49,87 @@ async def upload_one_async(bucket_name, object_name, upload_size, chunk_size):
4249 print (f"Latency: { latency :.2f} seconds" )
4350 print (f"Throughput: { throughput :.2f} MB/s" )
4451
45- # def upload_one_sync(bucket_name, object_name, upload_size, chunk_size):
46- # """Wrapper to run the async upload_one in a new event loop."""
47- # asyncio.run(upload_one_async(bucket_name, object_name, upload_size, chunk_size))
48-
4952
50- # def main():
51- # parser = argparse.ArgumentParser()
52- # parser.add_argument("--bucket_name", type=str, default='chandrasiri-rs')
53- # parser.add_argument("--upload_size", type=int, default=1024 * 1024 * 1024) # 1 GiB
54- # parser.add_argument("--chunk_size", type=int, default=100 * 1024 * 1024) # 100 MiB
55- # parser.add_argument("--count", type=int, default=100)
56- # parser.add_argument("--start_object_num", type=int, default=0)
57- # parser.add_argument("-n", "--num_workers", type=int, default=2, help="Number of worker threads or processes.")
58- # parser.add_argument("--executor", type=str, choices=['thread', 'process'], default='process', help="Executor to use: 'thread' for ThreadPoolExecutor, 'process' for ProcessPoolExecutor")
59- # args = parser.parse_args()
53+ def upload_one_sync (bucket_name , object_name , upload_size , chunk_size ):
54+ """Wrapper to run the async upload_one in a new event loop."""
55+ asyncio .run (upload_one_async (bucket_name , object_name , upload_size , chunk_size ))
6056
61- # total_start_time = time.perf_counter()
6257
63- # ExecutorClass = ThreadPoolExecutor if args.executor == 'thread' else ProcessPoolExecutor
58+ def main ():
59+ parser = argparse .ArgumentParser ()
60+ parser .add_argument ("--bucket_name" , type = str , default = "chandrasiri-rs" )
61+ parser .add_argument ("--upload_size_mib" , type = int , default = 1024 ) # 1 GiB
62+ parser .add_argument ("--chunk_size_mib" , type = int , default = 100 ) # 100 MiB
63+ parser .add_argument ("--count" , type = int , default = 100 )
64+ parser .add_argument ("--start_object_num" , type = int , default = 0 )
65+ parser .add_argument (
66+ "-n" ,
67+ "--num_workers" ,
68+ type = int ,
69+ default = 2 ,
70+ help = "Number of worker threads or processes." ,
71+ )
72+ parser .add_argument (
73+ "--executor" ,
74+ type = str ,
75+ choices = ["thread" , "process" ],
76+ default = "process" ,
77+ help = "Executor to use: 'thread' for ThreadPoolExecutor, 'process' for ProcessPoolExecutor" ,
78+ )
79+ args = parser .parse_args ()
6480
65- # with ExecutorClass(max_workers=args.num_workers) as executor:
66- # futures = []
67- # for i in range(args.start_object_num, args.start_object_num + args.count):
68- # object_name = f"py-sdk-mb-mt-{i}"
69- # future = executor.submit(upload_one_sync, args.bucket_name, object_name, args.upload_size, args.chunk_size)
70- # futures.append(future)
81+ total_start_time = time .perf_counter ()
7182
72- # for future in futures:
73- # future.result() # wait for all workers to complete
83+ ExecutorClass = (
84+ ThreadPoolExecutor if args .executor == "thread" else ProcessPoolExecutor
85+ )
7486
75- # total_end_time = time.perf_counter()
76- # total_latency = total_end_time - total_start_time
77- # total_uploaded_bytes = args.upload_size * args.count
78- # aggregate_throughput = (total_uploaded_bytes / total_latency) / (1000 * 1000) # MB/s
87+ with ResourceMonitor () as m :
88+ with ExecutorClass (max_workers = args .num_workers ) as executor :
89+ futures = []
90+ for i in range (args .start_object_num , args .start_object_num + args .count ):
91+ object_name = f"py-sdk-mb-mt-{ i } "
92+ future = executor .submit (
93+ upload_one_sync ,
94+ args .bucket_name ,
95+ object_name ,
96+ args .upload_size_mib * 1024 * 1024 ,
97+ args .chunk_size_mib * 1024 * 1024 ,
98+ )
99+ futures .append (future )
100+
101+ for future in futures :
102+ future .result () # wait for all workers to complete
103+
104+
105+ total_end_time = time .perf_counter ()
106+ total_latency = total_end_time - total_start_time
107+ total_uploaded_bytes = args .upload_size_mib * 1024 * 1024 * args .count
108+ aggregate_throughput = (total_uploaded_bytes / total_latency ) / (
109+ 1000 * 1000
110+ ) # MB/s
111+
112+ print ("\n --- Aggregate Results ---" )
113+ print (f"Total objects uploaded: { args .count } " )
114+ print (f"Total data uploaded: { total_uploaded_bytes / (1024 * 1024 * 1024 ):.2f} GiB" )
115+ print (f"Total time taken: { total_latency :.2f} seconds" )
116+ print (f"Aggregate throughput: { aggregate_throughput :.2f} MB/s" )
117+
118+ print ("\n --- Resource Monitor Results ---" )
119+ print (f"Max CPU: { m .max_cpu } " )
120+ print (f"Max Memory: { m .max_mem } " )
121+ print (f"Throughput (MB/s): { m .throughput_mb_s } " )
122+ print (f"vCPUs: { m .vcpus } " )
79123
80- # print("\n--- Aggregate Results ---")
81- # print(f"Total objects uploaded: {args.count}")
82- # print(f"Total data uploaded: {total_uploaded_bytes / (1024*1024*1024):.2f} GiB")
83- # print(f"Total time taken: {total_latency:.2f} seconds")
84- # print(f"Aggregate throughput: {aggregate_throughput:.2f} MB/s")
85124
86125
87126if __name__ == "__main__" :
88- parser = argparse .ArgumentParser ()
89- parser .add_argument ("--bucket_name" , type = str , default = 'chandrasiri-rs' )
90- # parser.add_argument("--object_suffix", type=str, required=True)
91- parser .add_argument ("--upload_size" , type = int , default = 1024 * 1024 * 1024 ) # 1 GiB
92- parser .add_argument ("--chunk_size" , type = int , default = 100 * 1024 * 1024 ) # 100 MiB
93- args = parser .parse_args ()
94- object_name = f'upload-test-{ str (uuid .uuid4 ())[:4 ]} '
95- asyncio .run (upload_one_async (args .bucket_name , object_name , args .upload_size , args .chunk_size ))
127+ main ()
128+ # parser = argparse.ArgumentParser()
129+ # parser.add_argument("--bucket_name", type=str, default='chandrasiri-rs')
130+ # # parser.add_argument("--object_suffix", type=str, required=True)
131+ # parser.add_argument("--upload_size", type=int, default=1024 * 1024 * 1024) # 1 GiB
132+ # parser.add_argument("--chunk_size", type=int, default=100 * 1024 * 1024) # 100 MiB
133+ # args = parser.parse_args()
134+ # object_name = f'upload-test-{str(uuid.uuid4())[:4]}'
135+ # asyncio.run(upload_one_async(args.bucket_name, object_name, args.upload_size, args.chunk_size))
0 commit comments