在Python中優(yōu)化I/O工作負(fù)載通常涉及了解瓶頸所在,然后應(yīng)用策略來減少或管理這些瓶頸。分析是這個(gè)過程中至關(guān)重要的一步,因?yàn)樗兄诖_定代碼中資源使用最多的部分。
以下是通過分析來優(yōu)化Python中的I/O工作負(fù)載的逐步指南:
確定I/O工作負(fù)載
首先,理解你的I/O工作負(fù)載的類型是很重要的。它們是否涉及磁盤I/O,例如文件讀寫操作,網(wǎng)絡(luò)I/O,包括通過網(wǎng)絡(luò)傳輸數(shù)據(jù),或者數(shù)據(jù)庫I/O,包括數(shù)據(jù)庫交互?每個(gè)類別都適用不同的優(yōu)化技術(shù)。本文將介紹與網(wǎng)絡(luò)和文件讀寫操作相關(guān)的I/O瓶頸。
使用分析工具
有幾種可用于分析Python代碼的工具:
cProfile
cProfile是Python中最常用的分析器。由于它是一個(gè)帶有可管理開銷的C擴(kuò)展,因此通常建議大多數(shù)用戶使用它,適用于運(yùn)行時(shí)間較長(zhǎng)的程序的分析。它被廣泛使用,原因有以下幾點(diǎn):
內(nèi)置且標(biāo)準(zhǔn):cProfile是Python標(biāo)準(zhǔn)庫的一部分,這意味著它在任何標(biāo)準(zhǔn)Python安裝中都可以直接使用,無需額外的包。
開銷較低:作為一個(gè)C擴(kuò)展,與一些純Python分析器相比,cProfile的開銷相對(duì)較低。這個(gè)特性使得它適用于需要長(zhǎng)時(shí)間運(yùn)行的應(yīng)用程序的分析,其中分析器對(duì)性能的影響是一個(gè)關(guān)注點(diǎn)。
通用分析:cProfile適用于大多數(shù)分析需求,平衡了詳細(xì)程度和可用性。它可以給出函數(shù)執(zhí)行時(shí)間的逐個(gè)函數(shù)的詳細(xì)分解,主要用于識(shí)別性能瓶頸。
廣泛接受和社區(qū)支持:由于它是標(biāo)準(zhǔn)庫的一部分并且易于使用,cProfile擁有廣泛的用戶群體和社區(qū)支持。
盡管cProfile是最常用的分析器,但重要的是要注意,對(duì)于給定的任務(wù),最佳的分析器可能取決于項(xiàng)目的具體需求。例如,line_profiler適用于逐行分析,memory_profiler適用于需要關(guān)注內(nèi)存使用的情況。選擇分析器通常取決于您想要優(yōu)化應(yīng)用程序的特定方面。
Line_profiler
Line_profiler是Python中的一個(gè)工具,提供對(duì)代碼逐行進(jìn)行分析,以便查看每行的性能。當(dāng)您試圖優(yōu)化代碼并需要了解瓶頸所在時(shí),這種精細(xì)度的分析非常有益。
Memory_profiler:如果您懷疑內(nèi)存使用與I/O效率有關(guān),這個(gè)分析器將非常有幫助。
分析分析數(shù)據(jù)
運(yùn)行完分析器后,分析數(shù)據(jù)以找出大部分時(shí)間花在哪里。通常,分析輸出會(huì)指示長(zhǎng)時(shí)間運(yùn)行的I/O操作、可批處理的重復(fù)I/O操作和可以消除的不必要的I/O操作。
應(yīng)用優(yōu)化策略
根據(jù)分析結(jié)果,您可以應(yīng)用不同的策略:
緩存:將數(shù)據(jù)存儲(chǔ)在內(nèi)存中,以避免重復(fù)的I/O操作。
批處理:將多個(gè)I/O操作合并為一個(gè),以減少開銷。
異步I/O:使用asyncio或其他異步編程技術(shù)進(jìn)行I/O操作,而無需阻塞主線程。
緩沖:對(duì)于磁盤I/O,使用緩沖區(qū)來減少I/O調(diào)用的次數(shù)。
數(shù)據(jù)壓縮:減小讀取或?qū)懭氲臄?shù)據(jù)大小可以提高I/O性能,特別適用于網(wǎng)絡(luò)和磁盤I/O。
并行處理:使用多線程或多進(jìn)程并行執(zhí)行I/O操作,特別適用于網(wǎng)絡(luò)I/O。
測(cè)試和迭代
應(yīng)用優(yōu)化后,再次對(duì)代碼進(jìn)行分析以查看影響。
繼續(xù)按以下過程進(jìn)行迭代:
優(yōu)化 - 分析 - 修改
其他注意事項(xiàng)
確保硬件不是限制因素。對(duì)于數(shù)據(jù)庫I/O,研究如何優(yōu)化數(shù)據(jù)庫查詢和索引。對(duì)于文件I/O,請(qǐng)考慮文件系統(tǒng)和運(yùn)行該文件系統(tǒng)的硬件。
文檔和社區(qū)資源
閱讀您所使用的分析工具的文檔,以獲得更詳細(xì)的指導(dǎo)。與Python社區(qū)或論壇互動(dòng),獲取專業(yè)建議和最佳實(shí)踐。
記住,優(yōu)化通常涉及權(quán)衡,重點(diǎn)放在能夠產(chǎn)生最大改進(jìn)的代碼部分上是至關(guān)重要的。
氣象站數(shù)據(jù)分析和分析優(yōu)化
我將以分析氣象站數(shù)據(jù)的示例為例。氣象站記錄每小時(shí)的溫度,并具有以下列。
"STATION","DATE","SOURCE","LATITUDE","LONGITUDE","ELEVATION","NAME","REPORT_TYPE","CALL_SIGN","QUALITY_CONTROL","WND","CIG","VIS","TMP","DEW","SLP","AA1","AA2","AA3","AJ1","KA1","KA2","OC1","OD1","OD2","REM"
我們的分析中,需要關(guān)注"STATION"和"TMP"這兩列。
我將按照以下步驟進(jìn)行操作:
創(chuàng)建一個(gè)Python程序,接受參數(shù)(氣象站列表(用逗號(hào)分隔),年份范圍(開始年份和結(jié)束年份,用連字符分隔))。
下載氣象站數(shù)據(jù)作為CSV文件。
解析CSV文件,并獲取所提供參數(shù)中氣象站列表和年份范圍內(nèi)的所有溫度。
找到年份范圍內(nèi)各個(gè)氣象站的最高溫度、最低溫度和平均溫度。
對(duì)代碼進(jìn)行分析優(yōu)化。
分析I/O瓶頸。
實(shí)現(xiàn)本地緩存。
分析輸出和運(yùn)行時(shí)間。
通過以上步驟,可以對(duì)氣象站數(shù)據(jù)進(jìn)行分析,并找到給定年份范圍內(nèi)各個(gè)氣象站的溫度統(tǒng)計(jì)信息。同時(shí),在分析過程中也可以進(jìn)行代碼優(yōu)化,以減少I/O瓶頸,并實(shí)現(xiàn)本地緩存以提高運(yùn)行效率。
沒有本地緩存的代碼
此程序下載指定氣象站的天氣數(shù)據(jù),并計(jì)算給定年份的低溫和高溫天氣:
示例一:
import csv import sys import requests import collections from statistics import mean # 此功能下載站點(diǎn)/年的天氣數(shù)據(jù),并將輸出寫入csv文件 def download_weather_station_data(station, year): my_url = generic_url.format(station=station, year=year) req = requests.get(my_url) if req.status_code != 200: return with open(generic_file.format(station=station, year=year), 'w') as sf: sf.write(req.text) # 此父功能下載給定電臺(tái)列表和年份范圍的天氣數(shù)據(jù) def download_all_weather_station_data(stations_list, start_year, end_year): for station in stations_list: for year in range(start_year, end_year + 1): download_weather_station_data(station, year) # 此函數(shù)從文件中獲取溫度詳細(xì)信息 def get_file_temperature(file_name): with open(file_name, 'r') as tf: reader = csv.reader(tf) header = next(reader) for row in reader: station = row[header.index("STATION")] temp = row[header.index("TMP")] temperature, status = temp.split(",") if int(status) != 1: continue temperature = int(temperature) / 10 yield temperature # 此父函數(shù)獲取給定站點(diǎn)和年份的所有溫度 def get_temperatures_all(stations_list, start_year, end_year): temperatures = collections.defaultdict(list) for station in stations_list: for year in range(start_year, end_year + 1): for temperature in get_file_temperature(generic_file.format(station=station, year=year)): temperatures[station].append(temperature) return temperatures # 此函數(shù)用于獲取給定年份內(nèi)電站的最高/最低/平均溫度 def get_temperatures(lst_temperatures, calc_mode): result = {} for mode in calc_mode: if mode == 'max': result[mode] = {station: max(temperatures) for station, temperatures in lst_temperatures.items()} elif mode == 'min': result[mode] = {station: min(temperatures) for station, temperatures in lst_temperatures.items()} else: result[mode] = {station: mean(temperatures) for station, temperatures in lst_temperatures.items()} return result # 主要功能 if __name__ := "__main__": stations = sys.argv[1].split(",") years = [int(year) for year in sys.argv[2].split("-")] first_year = years[0] last_year = years[1] generic_url = "https://www.ncei.noaa.gov/data/global-hourly/access/{year}/{station}.csv" generic_file = "Weather_station_{station}_{year}.csv" download_all_weather_station_data(stations, first_year, last_year) temperatures_all = get_temperatures_all(stations, first_year, last_year) temperatures_values = get_temperatures(temperatures_all, ['max', 'min', 'avg']) print(f"溫度為 {temperatures_values}")
示例二
import csv import requests def download_weather_data(stations, start_year, end_year): url = "https://example.com/weatherdata.csv" # Replace with the actual URL to download the weather data response = requests.get(url) with open("weather_data.csv", "wb") as file: file.write(response.content) def parse_weather_data(stations, start_year, end_year): temperatures = [] with open("weather_data.csv", "r") as file: reader = csv.DictReader(file) for row in reader: station = row["STATION"] year = int(row["DATE"][:4]) temperature = float(row["TMP"]) if station in stations and start_year <= year <= end_year: temperatures.append(temperature) return temperatures def calculate_statistics(temperatures): min_temp = min(temperatures) max_temp = max(temperatures) avg_temp = sum(temperatures) / len(temperatures) return min_temp, max_temp, avg_temp # Example usage stations = ["station1", "station2", "station3"] start_year = 2010 end_year = 2020 download_weather_data(stations, start_year, end_year) temperatures = parse_weather_data(stations, start_year, end_year) min_temp, max_temp, avg_temp = calculate_statistics(temperatures) print(f"Min Temperature: {min_temp}") print(f"Max Temperature: {max_temp}") print(f"Avg Temperature: {avg_temp}")
這段代碼定義了下載天氣數(shù)據(jù)、解析CSV文件、計(jì)算統(tǒng)計(jì)數(shù)據(jù)的函數(shù),并提供了一個(gè)使用示例。但是,它不包括任何本地緩存機(jī)制。
為了優(yōu)化I/O工作負(fù)載,我們可以引入本地緩存,以避免每次程序運(yùn)行時(shí)下載天氣數(shù)據(jù)。這可以通過在發(fā)出下載請(qǐng)求之前檢查CSV文件是否已經(jīng)存在來完成。如果文件存在,則可以重用該文件,而不是重新下載。
執(zhí)行了代碼并獲得了期望的輸出
python load_weather_data.py "01480099999,02110099999,02243099999" 2018-2023
輸出結(jié)果如下:
The temperatures are {'max': {'01480099999': 33.5, '02110099999': 29.6, '02243099999': 32.0}, 'min': {'01480099999': -20.4, '02110099999': -39.5, '02243099999': -32.1}, 'avg': {'01480099999': 7.145012712693135, '02110099999': 0.23863829994401306, '02243099999': 3.383049058515579}}
使用CProfile分析代碼:
python -m cProfile -s cumulative load_weather_data.py "01480099999,02110099999,02243099999" 2018-2023 > load_weather_data_profile.txt
以上命令將使用CProfile對(duì)代碼進(jìn)行分析,并將分析結(jié)果保存到`load_weather_data_profile.txt`文件中。
The temperatures are {'max': {'01480099999': 33.5, '02110099999': 29.6, '02243099999': 32.0}, 'min': {'01480099999': -20.4, '02110099999': -39.5, '02243099999': -32.1}, 'avg': {'01480099999': 7.1538004828081165, '02110099999': 0.23863829994401306, '02243099999': 3.383049058515579}} 1422783 function calls (1416758 primitive calls) in 17.250 seconds Ordered by: cumulative time ncalls tottime percall cumtime percall filename:lineno(function) 181/1 0.002 0.000 17.250 17.250 {built-in method builtins.exec} 1 0.000 0.000 17.250 17.250 load_weather_data.py:1(<module>) 1 0.003 0.003 16.241 16.241 load_weather_data.py:23(download_all_weather_station_data) 18 0.003 0.000 16.221 0.901 load_weather_data.py:12(download_weather_station_data)
函數(shù)調(diào)用download_all_weather_station_data占用了最多的運(yùn)行時(shí)間,有優(yōu)化I/O的空間。
由于數(shù)據(jù)是靜態(tài)的,一旦生成了CSV文件,就沒有必要再次生成。
下面的程序經(jīng)過優(yōu)化,如果已經(jīng)生成了文件,則不會(huì)再次生成。以下是優(yōu)化后的代碼示例:
"""此程序下載指定電臺(tái)的天氣數(shù)據(jù)并計(jì)算給定年份的低溫和高溫天氣""" import os import csv import sys import fnmatch import requests import collections from statistics import mean # #此功能下載站點(diǎn)/年的天氣數(shù)據(jù),并將輸出寫入csv文件 def download_weather_station_data(station, year): my_url = generic_url.format(station=station, year=year) req = requests.get(my_url) if req.status_code != 200: return with open(generic_file.format(station=station, year=year), 'w') as sf: sf.write(req.text) # 此父功能下載給定電臺(tái)列表和年份范圍的天氣數(shù)據(jù) def download_all_weather_station_data(stations_list, start_year, end_year): for station in stations_list: for year in range(start_year, end_year + 1): if not os.path.exists(generic_file.format(station=station, year=year)): download_weather_station_data(station, year) # 此函數(shù)從文件中獲取溫度詳細(xì)信息 def get_file_temperature(file_name): with open(file_name, 'r') as tf: reader = csv.reader(tf) header = next(reader) for row in reader: station = row[header.index("STATION")] temp = row[header.index("TMP")] temperature, status = temp.split(",") if int(status) != 1: continue temperature = int(temperature) / 10 yield temperature #此父函數(shù)獲取給定站點(diǎn)和年份的所有溫度 def get_temperatures_all(stations_list, start_year, end_year): temperatures = collections.defaultdict(list) for station in stations_list: for year in range(start_year, end_year + 1): if os.path.exists(generic_file.format(station=station, year=year)): for temperature in get_file_temperature(generic_file.format(station=station, year=year)): temperatures[station].append(temperature) return temperatures # 此函數(shù)用于獲取給定年份內(nèi)電站的最高/最低/平均溫度 def get_temperatures(lst_temperatures, calc_mode): result = {} for mode in calc_mode: if mode == 'max': result[mode] = {station: max(temperatures) for station, temperatures in lst_temperatures.items()} elif mode == 'min': result[mode] = {station: min(temperatures) for station, temperatures in lst_temperatures.items()} else: result[mode] = {station: mean(temperatures) for station, temperatures in lst_temperatures.items()} return result # 主要功能 if __name__ := "__main__": stations = sys.argv[1].split(",") years = [int(year) for year in sys.argv[2].split("-")] first_year = years[0] last_year = years[1] generic_url = "https://www.ncei.noaa.gov/data/global-hourly/access/{year}/{station}.csv" generic_file = "Weather_station_{station}_{year}.csv" current_directory = os.getcwd() download_all_weather_station_data(stations, first_year, last_year) count = len(fnmatch.filter(os.listdir(current_directory), '*.csv')) if count > 0: temperatures_all = get_temperatures_all(stations, first_year, last_year) temperatures_values = get_temperatures(temperatures_all, ['max', 'min', 'avg']) print(f"溫度為 {temperatures_values}") else: print(f"There are no file(s) available for the given stations {sys.argv[1]} and years {sys.argv[2]}")
示例二
import os.path def download_weather_station_data(station, year): # Check if the CSV file already exists csv_filename = f"{station}_{year}.csv" if os.path.isfile(csv_filename): print(f"CSV file for {station} and {year} already exists. Skipping download.") return # Download the weather station data as a CSV # ... (code to download the data) print(f"Downloaded CSV for {station} and {year}") def download_all_weather_station_data(stations, start_year, end_year): years = list(range(start_year, end_year+1)) for station in stations: for year in years: download_weather_station_data(station, year) # Usage example stations = ["01480099999", "02110099999", "02243099999"] start_year = 2018 end_year = 2023 download_all_weather_station_data(stations, start_year, end_year)
在上述代碼中,我們?cè)赿ownload_weather_station_data函數(shù)中添加了檢查邏輯,以驗(yàn)證是否已經(jīng)生成了CSV文件。如果文件已存在,則會(huì)打印一條消息并跳過下載過程。
這樣的優(yōu)化確保只有在需要時(shí)才進(jìn)行文件下載,避免了重復(fù)的I/O操作。
執(zhí)行了代碼并獲得了所需的輸出
python load_weather_data_cache.py "01480099999,02110099999,02243099999" 2018-2023
輸出結(jié)果如下:
The temperatures are {'max': {'01480099999': 33.5, '02110099999': 29.6, '02243099999': 32.0}, 'min': {'01480099999': -20.4, '02110099999': -39.5, '02243099999': -32.1}, 'avg': {'01480099999': 7.145012712693135, '02110099999': 0.2386..., '02243099999': 3.383049058515579}}
使用CProfile分析代碼:
python -m cProfile -s cumulative load_weather_data_cache.py "01480099999,02110099999,02243099999" 2018-2023 > load_weather_data_cache_profile.txt
以上命令將使用CProfile對(duì)代碼進(jìn)行分析,并將分析結(jié)果保存到`load_weather_data_cache_profile.txt`文件中。
在分析結(jié)果中,可以注意到函數(shù)調(diào)用`download_all_weather_station_data`不再是最耗時(shí)的部分。整體運(yùn)行時(shí)間減少了約16倍,這是一個(gè)顯著的性能提升。
結(jié)論
正如本示例所展示的,緩存有能力將代碼加速數(shù)倍。然而,管理緩存可能會(huì)帶來一些挑戰(zhàn),并且常常會(huì)導(dǎo)致錯(cuò)誤。在給定的示例中,文件隨時(shí)間保持不變,但值得注意的是,在許多情況下,緩存數(shù)據(jù)可能會(huì)發(fā)生變化。在這種情況下,負(fù)責(zé)緩存管理的代碼必須能夠識(shí)別和處理這些變化。
緩存是一個(gè)強(qiáng)大的工具,可以提高代碼的性能,但在使用時(shí)需要權(quán)衡其優(yōu)劣,并考慮到潛在的緩存失效問題。適當(dāng)?shù)毓芾砭彺娌⒋_保其有效性是確保代碼正確運(yùn)行和高效執(zhí)行的關(guān)鍵。文章來源:http://www.zghlxwxcb.cn/article/625.html
關(guān)鍵詞:Python I/O優(yōu)化,分析工具,優(yōu)化策略,瓶頸分析,cProfile,Line_profiler,Memory_profiler,緩存,批處理,異步I/O,數(shù)據(jù)壓縮,并行處理,氣象站數(shù)據(jù)分析文章來源地址http://www.zghlxwxcb.cn/article/625.html
到此這篇關(guān)于優(yōu)化Python中的I/O工作負(fù)載:逐步指南以及常用分析工具和優(yōu)化策略的文章就介紹到這了,更多相關(guān)內(nèi)容可以在右上角搜索或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!