Skip to content

Commit 786df5b

Browse files
committed
support vsifile IO support
1 parent dd3eaf2 commit 786df5b

File tree

3 files changed

+71
-13
lines changed

3 files changed

+71
-13
lines changed

tilebench/__init__.py

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
log.add(sys.stderr, format=fmt)
2020

2121

22-
def parse_logs(logs: List[str]) -> Dict[str, Any]:
22+
def parse_rasterio_io_logs(logs: List[str]) -> Dict[str, Any]:
2323
"""Parse Rasterio and CURL logs."""
2424
# HEAD
2525
head_requests = len([line for line in logs if "CURL_INFO_HEADER_OUT: HEAD" in line])
@@ -53,25 +53,67 @@ def parse_logs(logs: List[str]) -> Dict[str, Any]:
5353
}
5454

5555

56+
def parse_vsifile_io_logs(logs: List[str]) -> Dict[str, Any]:
57+
"""Parse VSIFILE IO logs."""
58+
# HEAD
59+
head_requests = len(
60+
[line for line in logs if "VSIFILE_INFO_HEADER_OUT: HEAD" in line]
61+
)
62+
head_summary = {
63+
"count": head_requests,
64+
}
65+
66+
# GET
67+
all_get_requests = len(
68+
[line for line in logs if "VSIFILE_INFO_HEADER_OUT: GET" in line]
69+
)
70+
71+
get_requests = [line for line in logs if "VSIFILE: Downloading: " in line]
72+
73+
get_values_str = []
74+
for get in get_requests:
75+
get_values_str.extend(get.split("VSIFILE: Downloading: ")[1].split(", "))
76+
77+
get_values = [list(map(int, r.split("-"))) for r in get_values_str]
78+
data_transfer = sum([j - i + 1 for i, j in get_values])
79+
80+
get_summary = {
81+
"count": all_get_requests,
82+
"bytes": data_transfer,
83+
"ranges": get_values_str,
84+
}
85+
86+
warp_kernel = [line.split(" ")[-2:] for line in logs if "GDALWarpKernel" in line]
87+
88+
return {
89+
"HEAD": head_summary,
90+
"GET": get_summary,
91+
"WarpKernels": warp_kernel,
92+
}
93+
94+
5695
def profile(
5796
kernels: bool = False,
5897
add_to_return: bool = False,
5998
quiet: bool = False,
6099
raw: bool = False,
61100
cprofile: bool = False,
62101
config: Optional[Dict] = None,
102+
io="rasterio",
63103
):
64104
"""Profiling."""
105+
if io not in ["rasterio", "vsifile"]:
106+
raise ValueError(f"Unsupported {io} IO backend")
65107

66108
def wrapper(func: Callable):
67109
"""Wrap a function."""
68110

69111
def wrapped_f(*args, **kwargs):
70112
"""Wrapped function."""
71-
rio_stream = StringIO()
72-
logger = logging.getLogger("rasterio")
113+
io_stream = StringIO()
114+
logger = logging.getLogger(io)
73115
logger.setLevel(logging.DEBUG)
74-
handler = logging.StreamHandler(rio_stream)
116+
handler = logging.StreamHandler(io_stream)
75117
logger.addHandler(handler)
76118

77119
gdal_config = config or {}
@@ -88,10 +130,15 @@ def wrapped_f(*args, **kwargs):
88130
logger.removeHandler(handler)
89131
handler.close()
90132

91-
logs = rio_stream.getvalue().splitlines()
133+
logs = io_stream.getvalue().splitlines()
92134
profile_lines = [p for p in profile_stream.getvalue().splitlines() if p]
93135

94-
results = parse_logs(logs)
136+
results = {}
137+
if io == "vsifile":
138+
results.update(parse_vsifile_io_logs(logs))
139+
else:
140+
results.update(parse_rasterio_io_logs(logs))
141+
95142
results["Timing"] = t.elapsed
96143

97144
if cprofile:

tilebench/middleware.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from starlette.requests import Request
1111
from starlette.types import ASGIApp, Message, Receive, Scope, Send
1212

13-
from tilebench import parse_logs
13+
from tilebench import parse_rasterio_io_logs
1414

1515

1616
class VSIStatsMiddleware(BaseHTTPMiddleware):
@@ -21,22 +21,24 @@ def __init__(
2121
app: ASGIApp,
2222
config: Optional[Dict] = None,
2323
exclude_paths: Optional[List] = None,
24+
io: str = "rasterio",
2425
) -> None:
2526
"""Init Middleware."""
2627
super().__init__(app)
2728
self.config: Dict = config or {}
2829
self.exclude_paths: List = exclude_paths or []
30+
self.io_backend = io
2931

3032
async def dispatch(self, request: Request, call_next):
3133
"""Add VSI stats in headers."""
3234

3335
if request.scope["path"] in self.exclude_paths:
3436
return await call_next(request)
3537

36-
rio_stream = StringIO()
37-
logger = logging.getLogger("rasterio")
38+
io_stream = StringIO()
39+
logger = logging.getLogger(self.io_backend)
3840
logger.setLevel(logging.DEBUG)
39-
handler = logging.StreamHandler(rio_stream)
41+
handler = logging.StreamHandler(io_stream)
4042
logger.addHandler(handler)
4143

4244
gdal_config = {"CPL_DEBUG": "ON", "CPL_CURL_VERBOSE": "TRUE"}
@@ -46,10 +48,10 @@ async def dispatch(self, request: Request, call_next):
4648
logger.removeHandler(handler)
4749
handler.close()
4850

49-
if rio_stream:
50-
rio_lines = rio_stream.getvalue().splitlines()
51+
if io_stream:
52+
io_lines = io_stream.getvalue().splitlines()
5153

52-
results = parse_logs(rio_lines)
54+
results = parse_rasterio_io_logs(io_lines)
5355
head_results = "head;count={count}".format(**results["HEAD"])
5456
get_results = "get;count={count};size={bytes}".format(**results["GET"])
5557
ranges_results = "ranges; values={}".format(

tilebench/scripts/cli.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ def cli():
103103
callback=options_to_dict,
104104
help="Reader Options.",
105105
)
106+
@click.option(
107+
"--io",
108+
"io_backend",
109+
type=click.Choice(["vsifile", "rasterio"], case_sensitive=True),
110+
help="IO Backend Options.",
111+
default="rasterio",
112+
)
106113
def profile(
107114
input,
108115
tile,
@@ -115,6 +122,7 @@ def profile(
115122
tms,
116123
config,
117124
reader_params,
125+
io_backend,
118126
):
119127
"""Profile Reader Tile read."""
120128
tilematrixset = default_tms
@@ -166,6 +174,7 @@ def profile(
166174
raw=add_stdout,
167175
cprofile=add_cprofile,
168176
config=config,
177+
io=io_backend,
169178
)
170179
def _read_tile(src_path: str, x: int, y: int, z: int, tilesize: int = 256):
171180
with DstReader(src_path, tms=tilematrixset, **reader_params) as cog:

0 commit comments

Comments
 (0)