Skip to content

Commit da5bdb9

Browse files
committed
improve pydantic performance
Updated manual perf tests to more closely reflect how data is constructed via DI
1 parent b5ed666 commit da5bdb9

4 files changed

Lines changed: 19 additions & 11 deletions

File tree

PerfTest.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#execution_count = 100_000_000
66
execution_count = 10_000_000
7+
execution_count = 1_000_000
78

89
def perf_test_di():
910
di = DIContainer()
@@ -25,11 +26,11 @@ def perf_test_di_pydantic():
2526

2627
def perf_test_manual():
2728
for _ in range(execution_count):
28-
_ = HelloResponse(body="Hello", goodbye=GoodbyeResponse(test=OtherResponse(test=0, body="")))
29+
_ = HelloResponse(*["Hello", GoodbyeResponse(*[OtherResponse(*[0, ""])])])
2930

3031
def perf_test_manual_pydantic():
3132
for _ in range(execution_count):
32-
_ = PydanticHelloResponse(body="Hello", goodbye=PydanticGoodbyeResponse(test=PydanticOtherResponse(test=0, body="")))
33+
_ = PydanticHelloResponse(**{"body": "Hello", "goodbye":PydanticGoodbyeResponse(**{"test":PydanticOtherResponse(**{"test":0, "body":""})})})
3334

3435
def perf_test_di_locate_all():
3536
di = DIContainer()

PythonDI.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,18 +106,19 @@ def __construct_type(self, di_constructor: TypeConstructor, object_type: type, p
106106
return object_type(*args)
107107

108108
def __construct_base_model(self, di_constructor: TypeConstructor, object_type: type, params=[]):
109-
obj = object_type()
110109
params_len = len(params)
111110

111+
args: dict[str, typing.Any] = {}
112+
112113
for i in range(len(di_constructor.constructor)):
113114
key = di_constructor.keys[i]
114115

115116
if (i < params_len):
116-
setattr(obj, key, params[i])
117+
args[key] = params[i]
117118
else:
118-
setattr(obj, key, self.locate(di_constructor.constructor[key]))
119+
args[key] = self.locate(di_constructor.constructor[key])
119120

120-
return obj
121+
return object_type(**args)
121122

122123
def __unregistered_default(self, object_type: type, params: list[typing.Any]):
123124
return object_type()

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,9 @@ To those whom it may concern, I have added performance tests which compare runni
102102
```
103103
Results
104104
Test Runtime (sec) Average (μs)
105-
DI test 22.10 2.21
106-
Pydantic DI test 146.79 14.68
107-
Manual test 7.03 0.70
108-
Pydantic Manual test 28.83 2.88
109-
Locate All test 14.09 1.41
105+
DI test 2.12 2.12
106+
Pydantic DI test 4.69 4.69
107+
Manual test 0.53 0.53
108+
Pydantic Manual test 2.90 2.90
109+
Locate All test 1.12 1.12
110110
```

test_DI.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ def test_locate():
1616
assert response.goodbye is not None # GoodbyeResponse not registered. default value used
1717
assert response.goodbye.test is None # TestResponse not located because GoodbyeResponse was default. test never populated
1818

19+
response.body = "test"
20+
21+
response2 = di.locate(HelloResponse, [])
22+
assert response != response2
23+
assert response.body != response2.body
24+
1925
def test_locate_unregistered_none():
2026
di = DIContainer(UnregisteredAction.NONE)
2127
di.register(HelloResponse)

0 commit comments

Comments
 (0)