Skip to content

Commit e5ce2b4

Browse files
committed
Optimize UDP buffer and WSGI/ASGI defaults
- Replace list with deque for UDP datagram buffer to get O(1) popleft instead of O(n) pop(0) operations - Define WSGI/ASGI environ/scope defaults as compile-time constants to avoid map allocation on each request
1 parent d0e5285 commit e5ce2b4

3 files changed

Lines changed: 46 additions & 33 deletions

File tree

priv/erlang_loop.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,7 +1208,7 @@ def __init__(self, loop, sock, protocol, address=None, extra=None):
12081208
self._sock = sock
12091209
self._protocol = protocol
12101210
self._address = address # Default remote address (for connected UDP)
1211-
self._buffer = [] # List of (data, addr) tuples
1211+
self._buffer = deque() # Deque of (data, addr) tuples for O(1) popleft
12121212
self._closing = False
12131213
self._conn_lost = 0
12141214
self._fileno = sock.fileno() # Cache fileno to avoid repeated calls
@@ -1286,14 +1286,14 @@ def _write_ready(self):
12861286
except (BlockingIOError, InterruptedError):
12871287
return
12881288
except OSError as exc:
1289-
self._buffer.pop(0)
1289+
self._buffer.popleft()
12901290
self._protocol.error_received(exc)
12911291
return
12921292
except Exception as exc:
12931293
self._fatal_error(exc, 'Fatal write error on datagram transport')
12941294
return
12951295

1296-
self._buffer.pop(0)
1296+
self._buffer.popleft()
12971297

12981298
self._loop.remove_writer(self._fileno)
12991299
if self._closing:

src/py_asgi.erl

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,20 @@
6464
scope_opts/0
6565
]).
6666

67+
%% Pre-defined ASGI scope defaults (compile-time constant for zero allocation)
68+
%% Note: raw_path is set dynamically based on path in ensure_scope_defaults/1
69+
-define(ASGI_SCOPE_DEFAULTS, #{
70+
type => <<"http">>,
71+
asgi => #{<<"version">> => <<"3.0">>, <<"spec_version">> => <<"2.3">>},
72+
http_version => <<"1.1">>,
73+
method => <<"GET">>,
74+
scheme => <<"http">>,
75+
query_string => <<>>,
76+
root_path => <<>>,
77+
headers => [],
78+
state => #{}
79+
}).
80+
6781
%% ASGI scope dictionary.
6882
-type scope() :: #{
6983
type := binary(),
@@ -149,18 +163,13 @@ build_scope(Scope, Opts) ->
149163
%%% ============================================================================
150164

151165
%% @private
152-
%% Ensure all required ASGI scope fields have defaults
166+
%% Ensure all required ASGI scope fields have defaults.
167+
%% Uses compile-time constant ?ASGI_SCOPE_DEFAULTS to avoid
168+
%% map allocation on each request.
153169
ensure_scope_defaults(Scope) ->
154-
Defaults = #{
155-
type => <<"http">>,
156-
asgi => #{<<"version">> => <<"3.0">>, <<"spec_version">> => <<"2.3">>},
157-
http_version => <<"1.1">>,
158-
method => <<"GET">>,
159-
scheme => <<"http">>,
160-
raw_path => maps:get(path, Scope, <<"/">>),
161-
query_string => <<>>,
162-
root_path => <<>>,
163-
headers => [],
164-
state => #{}
165-
},
166-
maps:merge(Defaults, Scope).
170+
%% raw_path defaults to path if not provided
171+
WithRawPath = case maps:is_key(raw_path, Scope) of
172+
true -> Scope;
173+
false -> Scope#{raw_path => maps:get(path, Scope, <<"/">>)}
174+
end,
175+
maps:merge(?ASGI_SCOPE_DEFAULTS, WithRawPath).

src/py_wsgi.erl

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,22 @@
5757
environ/0
5858
]).
5959

60+
%% Pre-defined WSGI environ defaults (compile-time constant for zero allocation)
61+
-define(WSGI_ENVIRON_DEFAULTS, #{
62+
<<"REQUEST_METHOD">> => <<"GET">>,
63+
<<"SCRIPT_NAME">> => <<>>,
64+
<<"PATH_INFO">> => <<"/">>,
65+
<<"QUERY_STRING">> => <<>>,
66+
<<"SERVER_NAME">> => <<"localhost">>,
67+
<<"SERVER_PORT">> => <<"80">>,
68+
<<"SERVER_PROTOCOL">> => <<"HTTP/1.1">>,
69+
<<"wsgi.version">> => {1, 0},
70+
<<"wsgi.url_scheme">> => <<"http">>,
71+
<<"wsgi.multithread">> => true,
72+
<<"wsgi.multiprocess">> => true,
73+
<<"wsgi.run_once">> => false
74+
}).
75+
6076
%% WSGI environ dictionary.
6177
-type environ() :: #{
6278
binary() => binary() | integer() | atom()
@@ -100,20 +116,8 @@ run(Module, Callable, Environ, Opts) ->
100116
%%% ============================================================================
101117

102118
%% @private
103-
%% Ensure all required WSGI environ fields have defaults
119+
%% Ensure all required WSGI environ fields have defaults.
120+
%% Uses compile-time constant ?WSGI_ENVIRON_DEFAULTS to avoid
121+
%% map allocation on each request.
104122
ensure_environ_defaults(Environ) ->
105-
Defaults = #{
106-
<<"REQUEST_METHOD">> => <<"GET">>,
107-
<<"SCRIPT_NAME">> => <<>>,
108-
<<"PATH_INFO">> => <<"/">>,
109-
<<"QUERY_STRING">> => <<>>,
110-
<<"SERVER_NAME">> => <<"localhost">>,
111-
<<"SERVER_PORT">> => <<"80">>,
112-
<<"SERVER_PROTOCOL">> => <<"HTTP/1.1">>,
113-
<<"wsgi.version">> => {1, 0},
114-
<<"wsgi.url_scheme">> => <<"http">>,
115-
<<"wsgi.multithread">> => true,
116-
<<"wsgi.multiprocess">> => true,
117-
<<"wsgi.run_once">> => false
118-
},
119-
maps:merge(Defaults, Environ).
123+
maps:merge(?WSGI_ENVIRON_DEFAULTS, Environ).

0 commit comments

Comments
 (0)