Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve NSubstitute performance #536

Merged
merged 15 commits into from
Apr 13, 2019
Merged

Conversation

zvirja
Copy link
Contributor

@zvirja zvirja commented Apr 8, 2019

I've reviewed the mocking container performance comparison and found that unacceptable that Moq and FakeItEasy beat us with such a big distance 😟 Therefore I spent a couple of weeks and tried to make NSubstitute a bit faster. Moq is still faster, but we are not that much behind now.

I'm happy it was possible to achieve boost with relatively small amount of internal changes. Also notice, that I drastically decreased amount of memory allocations.

I extended our benchmarks to have more and cover basic usage scenarios. Here you can find the performance difference (took me a 3-4 hours to actually run all the benchmarks twice 😮).

See benchmark results
|                              Method |  Branch | Runtime |       Mean |     Error |    StdDev |  Gen 0 | Allocated |
//
// Activation
//
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|       CreateAbstractClassSubstitute |  master |     Clr |   4.911 us | 0.0963 us | 0.1110 us | 1.4343 |   5.88 KB |
|       CreateAbstractClassSubstitute |      PR |     Clr |   3.948 us | 0.0188 us | 0.0176 us | 0.9842 |   4.05 KB |
|       CreateAbstractClassSubstitute |  master |    Core |   4.761 us | 0.0948 us | 0.2514 us | 2.0981 |   8.61 KB |
|       CreateAbstractClassSubstitute |      PR |    Core |   4.009 us | 0.0186 us | 0.0145 us | 1.0986 |   4.51 KB |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|            CreateDelegateSubstitute |  master |     Clr |   9.917 us | 0.1960 us | 0.1925 us | 1.5106 |   6.24 KB |
|            CreateDelegateSubstitute |      PR |     Clr |   9.522 us | 0.0887 us | 0.0741 us | 1.0681 |   4.42 KB |
|            CreateDelegateSubstitute |  master |    Core |   6.857 us | 0.0258 us | 0.0215 us | 2.1896 |   8.97 KB |
|            CreateDelegateSubstitute |      PR |    Core |   6.149 us | 0.0279 us | 0.0261 us | 1.1826 |   4.87 KB |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|           CreateInterfaceSubstitute |  master |     Clr |   4.540 us | 0.0294 us | 0.0230 us | 1.4954 |   6.14 KB |
|           CreateInterfaceSubstitute |      PR |     Clr |   4.516 us | 0.0139 us | 0.0130 us | 1.0529 |   4.32 KB |
|           CreateInterfaceSubstitute |  master |    Core |   4.811 us | 0.0189 us | 0.0176 us | 2.1667 |   8.88 KB |
|           CreateInterfaceSubstitute |      PR |    Core |   4.255 us | 0.0819 us | 0.0943 us | 1.1597 |   4.78 KB |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|    CreateNonAbstractClassSubstitute |  master |     Clr |   4.188 us | 0.0288 us | 0.0255 us | 1.4343 |   5.88 KB |
|    CreateNonAbstractClassSubstitute |      PR |     Clr |   3.893 us | 0.0420 us | 0.0351 us | 0.9842 |   4.05 KB |
|    CreateNonAbstractClassSubstitute |  master |    Core |   4.808 us | 0.0962 us | 0.0900 us | 2.0981 |   8.61 KB |
|    CreateNonAbstractClassSubstitute |      PR |    Core |   3.949 us | 0.0143 us | 0.0134 us | 1.0986 |   4.51 KB |
//
// ArgumentSpecificationUsage
//
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|       ConfigureArgumentWithAnyValue |  master |     Clr |   3.047 us | 0.0609 us | 0.0854 us | 0.2899 |    1.8 KB |
|       ConfigureArgumentWithAnyValue |      PR |     Clr |   2.002 us | 0.0477 us | 0.0446 us | 0.1678 |    1064 B |
|       ConfigureArgumentWithAnyValue |  master |    Core |   3.307 us | 0.0660 us | 0.1085 us | 0.2556 |   1.59 KB |
|       ConfigureArgumentWithAnyValue |      PR |    Core |   2.213 us | 0.0207 us | 0.0184 us | 0.1526 |     960 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|    ConfigureOutArgumentWithAnyValue |  master |     Clr |   3.202 us | 0.0716 us | 0.1026 us | 0.2899 |    1.8 KB |
|    ConfigureOutArgumentWithAnyValue |      PR |     Clr |   2.159 us | 0.0229 us | 0.0214 us | 0.1678 |    1064 B |
|    ConfigureOutArgumentWithAnyValue |  master |    Core |   3.503 us | 0.0534 us | 0.0474 us | 0.2556 |   1.59 KB |
|    ConfigureOutArgumentWithAnyValue |      PR |    Core |   2.405 us | 0.0139 us | 0.0116 us | 0.1526 |     960 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|    ConfigureRefArgumentWithAnyValue |  master |     Clr |   3.153 us | 0.0526 us | 0.0440 us | 0.2899 |    1.8 KB |
|    ConfigureRefArgumentWithAnyValue |      PR |     Clr |   2.314 us | 0.0253 us | 0.0211 us | 0.1678 |    1064 B |
|    ConfigureRefArgumentWithAnyValue |  master |    Core |   3.496 us | 0.0603 us | 0.0535 us | 0.2556 |   1.59 KB |
|    ConfigureRefArgumentWithAnyValue |      PR |    Core |   2.401 us | 0.0183 us | 0.0153 us | 0.1526 |     960 B |
//
// DispatchConfiguredMatchingCall
//
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|  DispatchAbstractClassProxyCall_Int |  master |     Clr |   2.995 us | 0.0585 us | 0.0601 us | 0.3548 |   2.18 KB |
|  DispatchAbstractClassProxyCall_Int |      PR |     Clr |   1.237 us | 0.0182 us | 0.0170 us | 0.0916 |     581 B |
|  DispatchAbstractClassProxyCall_Int |  master |    Core |   3.623 us | 0.0283 us | 0.0265 us | 0.3357 |   2.05 KB |
|  DispatchAbstractClassProxyCall_Int |      PR |    Core |   1.133 us | 0.0183 us | 0.0163 us | 0.0896 |     568 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
| DispatchAbstractClassProxyCall_Void |  master |     Clr |   3.717 us | 0.0262 us | 0.0245 us | 0.3815 |   2.34 KB |
| DispatchAbstractClassProxyCall_Void |      PR |     Clr |   2.031 us | 0.0273 us | 0.0255 us | 0.1144 |     717 B |
| DispatchAbstractClassProxyCall_Void |  master |    Core |   4.071 us | 0.0272 us | 0.0255 us | 0.3510 |   2.15 KB |
| DispatchAbstractClassProxyCall_Void |      PR |    Core |   1.996 us | 0.0183 us | 0.0171 us | 0.0992 |     696 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|   DispatchClassPartialProxyCall_Int |  master |     Clr |   4.183 us | 0.0828 us | 0.1952 us | 0.3891 |   2.42 KB |
|   DispatchClassPartialProxyCall_Int |      PR |     Clr |   2.135 us | 0.0296 us | 0.0262 us | 0.1297 |     829 B |
|   DispatchClassPartialProxyCall_Int |  master |    Core |   4.478 us | 0.0669 us | 0.0626 us | 0.3738 |    2.3 KB |
|   DispatchClassPartialProxyCall_Int |      PR |    Core |   2.052 us | 0.0401 us | 0.0394 us | 0.1297 |     824 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|  DispatchClassPartialProxyCall_Void |  master |     Clr |   4.668 us | 0.0511 us | 0.0478 us | 0.4196 |   2.58 KB |
|  DispatchClassPartialProxyCall_Void |      PR |     Clr |   3.071 us | 0.0300 us | 0.0235 us | 0.1526 |     965 B |
|  DispatchClassPartialProxyCall_Void |  master |    Core |   5.191 us | 0.0548 us | 0.0485 us | 0.3891 |   2.52 KB |
|  DispatchClassPartialProxyCall_Void |      PR |    Core |   2.996 us | 0.0322 us | 0.0252 us | 0.1411 |     952 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|            DispatchDelegateCall_Int |  master |     Clr |   2.662 us | 0.0573 us | 0.0858 us | 0.3433 |   2.11 KB |
|            DispatchDelegateCall_Int |      PR |     Clr |   0.929 us | 0.0162 us | 0.0144 us | 0.0820 |     517 B |
|            DispatchDelegateCall_Int |  master |    Core |   3.232 us | 0.0168 us | 0.0157 us | 0.3242 |   1.99 KB |
|            DispatchDelegateCall_Int |      PR |    Core |   0.899 us | 0.0070 us | 0.0059 us | 0.0801 |     520 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|           DispatchDelegateCall_Void |  master |     Clr |   3.337 us | 0.0196 us | 0.0184 us | 0.3700 |   2.28 KB |
|           DispatchDelegateCall_Void |      PR |     Clr |   1.765 us | 0.0139 us | 0.0130 us | 0.1030 |     653 B |
|           DispatchDelegateCall_Void |  master |    Core |   3.927 us | 0.0299 us | 0.0265 us | 0.3357 |   2.21 KB |
|           DispatchDelegateCall_Void |      PR |    Core |   1.656 us | 0.0179 us | 0.0159 us | 0.0896 |     600 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|      DispatchInterfaceProxyCall_Int |  master |     Clr |   2.698 us | 0.0540 us | 0.0739 us | 0.3433 |   2.11 KB |
|      DispatchInterfaceProxyCall_Int |      PR |     Clr |   0.955 us | 0.0186 us | 0.0174 us | 0.0820 |     517 B |
|      DispatchInterfaceProxyCall_Int |  master |    Core |   3.322 us | 0.0264 us | 0.0234 us | 0.3242 |   1.99 KB |
|      DispatchInterfaceProxyCall_Int |      PR |    Core |   0.918 us | 0.0602 us | 0.0047 us | 0.0801 |     520 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|     DispatchInterfaceProxyCall_Void |  master |     Clr |   3.264 us | 0.0219 us | 0.0204 us | 0.3700 |   2.28 KB |
|     DispatchInterfaceProxyCall_Void |      PR |     Clr |   1.793 us | 0.0174 us | 0.0155 us | 0.1030 |     653 B |
|     DispatchInterfaceProxyCall_Void |  master |    Core |   3.929 us | 0.0224 us | 0.0198 us | 0.3357 |   2.09 KB |
|     DispatchInterfaceProxyCall_Void |      PR |    Core |   1.702 us | 0.0218 us | 0.0204 us | 0.0896 |     600 B |
//
// DispatchConfiguredNonMatchingCall
//
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|  DispatchAbstractClassProxyCall_Int |  master |     Clr |   3.870 us | 0.0289 us | 0.0241 us | 0.4120 |   2.55 KB |
|  DispatchAbstractClassProxyCall_Int |      PR |     Clr |   1.561 us | 0.0123 us | 0.0115 us | 0.0744 |     469 B |
|  DispatchAbstractClassProxyCall_Int |  master |    Core |   4.373 us | 0.0213 us | 0.0189 us | 0.3738 |   2.44 KB |
|  DispatchAbstractClassProxyCall_Int |      PR |    Core |   1.502 us | 0.0178 us | 0.0167 us | 0.0725 |     456 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
| DispatchAbstractClassProxyCall_Void |  master |     Clr |   3.214 us | 0.0224 us | 0.0209 us | 0.2899 |   1.79 KB |
| DispatchAbstractClassProxyCall_Void |      PR |     Clr |   1.503 us | 0.0096 us | 0.0090 us | 0.0668 |     421 B |
| DispatchAbstractClassProxyCall_Void |  master |    Core |   3.476 us | 0.0173 us | 0.0153 us | 0.2708 |   1.73 KB |
| DispatchAbstractClassProxyCall_Void |      PR |    Core |   1.363 us | 0.0166 us | 0.0156 us | 0.0648 |     440 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|   DispatchClassPartialProxyCall_Int |  master |     Clr |   5.063 us | 0.0560 us | 0.0496 us | 0.4501 |   2.79 KB |
|   DispatchClassPartialProxyCall_Int |      PR |     Clr |   2.397 us | 0.0403 us | 0.0377 us | 0.1144 |     717 B |
|   DispatchClassPartialProxyCall_Int |  master |    Core |   5.510 us | 0.0318 us | 0.0282 us | 0.4120 |   2.69 KB |
|   DispatchClassPartialProxyCall_Int |      PR |    Core |   2.297 us | 0.0440 us | 0.0432 us | 0.1106 |     712 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|  DispatchClassPartialProxyCall_Void |  master |     Clr |   4.075 us | 0.0615 us | 0.0576 us | 0.3281 |   2.04 KB |
|  DispatchClassPartialProxyCall_Void |      PR |     Clr |   2.340 us | 0.0406 us | 0.0380 us | 0.1068 |     669 B |
|  DispatchClassPartialProxyCall_Void |  master |    Core |   4.262 us | 0.0814 us | 0.0871 us | 0.3128 |   1.92 KB |
|  DispatchClassPartialProxyCall_Void |      PR |    Core |   2.138 us | 0.0426 us | 0.0438 us | 0.1030 |     664 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|            DispatchDelegateCall_Int |  master |     Clr |   3.424 us | 0.0268 us | 0.0224 us | 0.4044 |   2.49 KB |
|            DispatchDelegateCall_Int |      PR |     Clr |   1.221 us | 0.0170 us | 0.0159 us | 0.0648 |     405 B |
|            DispatchDelegateCall_Int |  master |    Core |   3.963 us | 0.0108 us | 0.0085 us | 0.3662 |   2.38 KB |
|            DispatchDelegateCall_Int |      PR |    Core |   1.194 us | 0.0156 us | 0.0146 us | 0.0610 |     424 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|           DispatchDelegateCall_Void |  master |     Clr |   2.812 us | 0.0172 us | 0.0161 us | 0.2823 |   1.73 KB |
|           DispatchDelegateCall_Void |      PR |     Clr |   1.164 us | 0.0135 us | 0.0126 us | 0.0572 |     357 B |
|           DispatchDelegateCall_Void |  master |    Core |   3.226 us | 0.0290 us | 0.0257 us | 0.2594 |   1.61 KB |
|           DispatchDelegateCall_Void |      PR |    Core |   1.106 us | 0.0214 us | 0.0220 us | 0.0534 |     376 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|      DispatchInterfaceProxyCall_Int |  master |     Clr |   3.509 us | 0.0288 us | 0.0269 us | 0.4044 |   2.49 KB |
|      DispatchInterfaceProxyCall_Int |      PR |     Clr |   1.249 us | 0.0188 us | 0.0176 us | 0.0648 |     405 B |
|      DispatchInterfaceProxyCall_Int |  master |    Core |   4.048 us | 0.0252 us | 0.0210 us | 0.3662 |   2.38 KB |
|      DispatchInterfaceProxyCall_Int |      PR |    Core |   1.203 us | 0.0172 us | 0.0161 us | 0.0610 |     424 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|     DispatchInterfaceProxyCall_Void |  master |     Clr |   2.934 us | 0.0615 us | 0.1336 us | 0.2823 |   1.73 KB |
|     DispatchInterfaceProxyCall_Void |      PR |     Clr |   1.167 us | 0.0147 us | 0.0137 us | 0.0572 |     357 B |
|     DispatchInterfaceProxyCall_Void |  master |    Core |   3.167 us | 0.0196 us | 0.0174 us | 0.2594 |   1.61 KB |
|     DispatchInterfaceProxyCall_Void |      PR |    Core |   1.097 us | 0.0208 us | 0.0223 us | 0.0534 |     344 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
//
// DispatchNonConfiguredCall
//
|  DispatchAbstractClassProxyCall_Int |  master |     Clr |   3.821 us | 0.0359 us | 0.0336 us | 0.3624 |   2.23 KB |
|  DispatchAbstractClassProxyCall_Int |      PR |     Clr |   1.593 us | 0.0116 us | 0.0109 us | 0.0668 |     421 B |
|  DispatchAbstractClassProxyCall_Int |  master |    Core |   3.711 us | 0.0337 us | 0.0315 us | 0.3204 |   1.99 KB |
|  DispatchAbstractClassProxyCall_Int |      PR |    Core |   1.346 us | 0.0161 us | 0.0151 us | 0.0648 |     440 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
| DispatchAbstractClassProxyCall_Void |  master |     Clr |   2.905 us | 0.0223 us | 0.0209 us | 0.2480 |   1.53 KB |
| DispatchAbstractClassProxyCall_Void |      PR |     Clr |   1.376 us | 0.0128 us | 0.0120 us | 0.0668 |     421 B |
| DispatchAbstractClassProxyCall_Void |  master |    Core |   2.737 us | 0.0314 us | 0.0294 us | 0.2289 |   1.47 KB |
| DispatchAbstractClassProxyCall_Void |      PR |    Core |   1.287 us | 0.0144 us | 0.0135 us | 0.0648 |     440 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|   DispatchClassPartialProxyCall_Int |  master |     Clr |   4.605 us | 0.0473 us | 0.0442 us | 0.4044 |   2.47 KB |
|   DispatchClassPartialProxyCall_Int |      PR |     Clr |   2.298 us | 0.0449 us | 0.0398 us | 0.1068 |     669 B |
|   DispatchClassPartialProxyCall_Int |  master |    Core |   4.642 us | 0.0714 us | 0.0667 us | 0.3662 |   2.24 KB |
|   DispatchClassPartialProxyCall_Int |      PR |    Core |   2.084 us | 0.0401 us | 0.0430 us | 0.1030 |     664 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|  DispatchClassPartialProxyCall_Void |  master |     Clr |   3.819 us | 0.0309 us | 0.0289 us | 0.2861 |   1.77 KB |
|  DispatchClassPartialProxyCall_Void |      PR |     Clr |   2.358 us | 0.0392 us | 0.0367 us | 0.1068 |     669 B |
|  DispatchClassPartialProxyCall_Void |  master |    Core |   3.529 us | 0.0344 us | 0.0322 us | 0.2670 |   1.66 KB |
|  DispatchClassPartialProxyCall_Void |      PR |    Core |   2.053 us | 0.0398 us | 0.0426 us | 0.1030 |     664 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|            DispatchDelegateCall_Int |  master |     Clr |   3.240 us | 0.0209 us | 0.0195 us | 0.3510 |   2.17 KB |
|            DispatchDelegateCall_Int |      PR |     Clr |   1.112 us | 0.0047 us | 0.0037 us | 0.0572 |     357 B |
|            DispatchDelegateCall_Int |  master |    Core |   3.308 us | 0.0121 us | 0.0113 us | 0.3128 |   1.93 KB |
|            DispatchDelegateCall_Int |      PR |    Core |   1.069 us | 0.0210 us | 0.0225 us | 0.0534 |     344 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|           DispatchDelegateCall_Void |  master |     Clr |   2.868 us | 0.0324 us | 0.0303 us | 0.2365 |   1.47 KB |
|           DispatchDelegateCall_Void |      PR |     Clr |   1.113 us | 0.0157 us | 0.0147 us | 0.0572 |     357 B |
|           DispatchDelegateCall_Void |  master |    Core |   2.517 us | 0.0191 us | 0.0179 us | 0.2174 |   1.34 KB |
|           DispatchDelegateCall_Void |      PR |    Core |   1.026 us | 0.0195 us | 0.0216 us | 0.0534 |     376 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|      DispatchInterfaceProxyCall_Int |  master |     Clr |   3.046 us | 0.0201 us | 0.0188 us | 0.3510 |   2.17 KB |
|      DispatchInterfaceProxyCall_Int |      PR |     Clr |   1.137 us | 0.0136 us | 0.0127 us | 0.0572 |     357 B |
|      DispatchInterfaceProxyCall_Int |  master |    Core |   3.372 us | 0.0188 us | 0.0176 us | 0.3128 |   1.93 KB |
|      DispatchInterfaceProxyCall_Int |      PR |    Core |   1.068 us | 0.0214 us | 0.0238 us | 0.0534 |     376 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|     DispatchInterfaceProxyCall_Void |  master |     Clr |   2.598 us | 0.0322 us | 0.0286 us | 0.2365 |   1.47 KB |
|     DispatchInterfaceProxyCall_Void |      PR |     Clr |   1.089 us | 0.0157 us | 0.0147 us | 0.0572 |     357 B |
|     DispatchInterfaceProxyCall_Void |  master |    Core |   2.566 us | 0.0197 us | 0.0184 us | 0.2174 |   1.34 KB |
|     DispatchInterfaceProxyCall_Void |      PR |    Core |   1.010 us | 0.0193 us | 0.0214 us | 0.0534 |     376 B |
//
// ToStringCall
//
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|                    AbstractToString |  master |     Clr | 37.6144 ns | 0.1120 ns | 0.0993 ns | 0.0247 |     104 B |
|                    AbstractToString |      PR |     Clr | 38.2191 ns | 0.1452 ns | 0.1359 ns | 0.0247 |     104 B |
|                    AbstractToString |  master |    Core | 47.3229 ns | 0.0274 ns | 0.0243 ns | 0.0247 |     104 B |
|                    AbstractToString |      PR |    Core | 47.8065 ns | 0.1312 ns | 0.1227 ns | 0.0247 |     104 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|  ClassWithoutToStringImplementation |  master |     Clr | 37.5720 ns | 0.0996 ns | 0.0832 ns | 0.0247 |     104 B |
|  ClassWithoutToStringImplementation |      PR |     Clr | 38.1036 ns | 0.0435 ns | 0.0407 ns | 0.0247 |     104 B |
|  ClassWithoutToStringImplementation |  master |    Core | 44.9767 ns | 0.0367 ns | 0.0343 ns | 0.0247 |     104 B |
|  ClassWithoutToStringImplementation |      PR |    Core | 45.0810 ns | 0.0481 ns | 0.0450 ns | 0.0247 |     104 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|     ClassWithToStringImplementation |  master |     Clr |  0.5500 ns | 0.0233 ns | 0.0218 ns |      - |       0 B |
|     ClassWithToStringImplementation |      PR |     Clr |  0.5281 ns | 0.0068 ns | 0.0060 ns |      - |       0 B |
|     ClassWithToStringImplementation |  master |    Core |  0.5239 ns | 0.0036 ns | 0.0024 ns |      - |       0 B |
|     ClassWithToStringImplementation |      PR |    Core |  0.7577 ns | 0.0331 ns | 0.0310 ns |      - |       0 B |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|                   InterfaceToString |  master |     Clr | 40.5204 ns | 0.0567 ns | 0.0502 ns | 0.0247 |     104 B |
|                   InterfaceToString |      PR |     Clr | 41.2793 ns | 0.0617 ns | 0.0577 ns | 0.0247 |     104 B |
|                   InterfaceToString |  master |    Core | 51.9295 ns | 0.0610 ns | 0.0541 ns | 0.0247 |     104 B |
|                   InterfaceToString |      PR |    Core | 51.5911 ns | 0.1731 ns | 0.1619 ns | 0.0247 |     104 B |
//
// VerifyReceivedCall
//
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|    VerifyAbstractClassProxyCall_Int |  master |     Clr |   4.779 us | 0.0180 us | 0.0168 us | 0.8469 |   3.49 KB |
|    VerifyAbstractClassProxyCall_Int |      PR |     Clr |   3.740 us | 0.0212 us | 0.0188 us | 0.6561 |    2.7 KB |
|    VerifyAbstractClassProxyCall_Int |  master |    Core |   5.190 us | 0.0257 us | 0.0215 us | 0.7401 |   3.04 KB |
|    VerifyAbstractClassProxyCall_Int |      PR |    Core |   3.666 us | 0.0137 us | 0.0128 us | 0.5722 |   2.36 KB |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|   VerifyAbstractClassProxyCall_Void |  master |     Clr |   4.471 us | 0.0177 us | 0.0165 us | 0.7858 |   3.24 KB |
|   VerifyAbstractClassProxyCall_Void |      PR |     Clr |   3.611 us | 0.0113 us | 0.0106 us | 0.6561 |    2.7 KB |
|   VerifyAbstractClassProxyCall_Void |  master |    Core |   4.343 us | 0.0123 us | 0.0115 us | 0.6866 |   2.83 KB |
|   VerifyAbstractClassProxyCall_Void |      PR |    Core |   3.616 us | 0.0180 us | 0.0168 us | 0.5722 |   2.36 KB |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|     VerifyClassPartialProxyCall_Int |  master |     Clr |   4.975 us | 0.0139 us | 0.0116 us | 0.9079 |   3.73 KB |
|     VerifyClassPartialProxyCall_Int |      PR |     Clr |   3.861 us | 0.0098 us | 0.0092 us | 0.7172 |   2.95 KB |
|     VerifyClassPartialProxyCall_Int |  master |    Core |   5.243 us | 0.0095 us | 0.0084 us | 0.8011 |   3.29 KB |
|     VerifyClassPartialProxyCall_Int |      PR |    Core |   3.874 us | 0.0046 us | 0.0038 us | 0.6332 |   2.61 KB |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|    VerifyClassPartialProxyCall_Void |  master |     Clr |   4.448 us | 0.0053 us | 0.0044 us | 0.8469 |   3.48 KB |
|    VerifyClassPartialProxyCall_Void |      PR |     Clr |   3.768 us | 0.0104 us | 0.0097 us | 0.7172 |   2.95 KB |
|    VerifyClassPartialProxyCall_Void |  master |    Core |   4.535 us | 0.0097 us | 0.0091 us | 0.7477 |   3.08 KB |
|    VerifyClassPartialProxyCall_Void |      PR |    Core |   3.824 us | 0.0050 us | 0.0042 us | 0.6332 |   2.61 KB |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|              VerifyDelegateCall_Int |  master |     Clr |   4.571 us | 0.0149 us | 0.0140 us | 0.8316 |   3.42 KB |
|              VerifyDelegateCall_Int |      PR |     Clr |   3.495 us | 0.0187 us | 0.0175 us | 0.6409 |   2.63 KB |
|              VerifyDelegateCall_Int |  master |    Core |   4.881 us | 0.0108 us | 0.0096 us | 0.7172 |   2.97 KB |
|              VerifyDelegateCall_Int |      PR |    Core |   3.407 us | 0.0112 us | 0.0105 us | 0.5569 |   2.29 KB |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|             VerifyDelegateCall_Void |  master |     Clr |   4.086 us | 0.0082 us | 0.0064 us | 0.7706 |   3.17 KB |
|             VerifyDelegateCall_Void |      PR |     Clr |   3.316 us | 0.0076 us | 0.0071 us | 0.6409 |   2.63 KB |
|             VerifyDelegateCall_Void |  master |    Core |   4.284 us | 0.0141 us | 0.0125 us | 0.6714 |   2.76 KB |
|             VerifyDelegateCall_Void |      PR |    Core |   3.367 us | 0.0068 us | 0.0057 us | 0.5569 |   2.29 KB |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|        VerifyInterfaceProxyCall_Int |  master |     Clr |   4.364 us | 0.0223 us | 0.0209 us | 0.8316 |   3.43 KB |
|        VerifyInterfaceProxyCall_Int |      PR |     Clr |   3.288 us | 0.0130 us | 0.0108 us | 0.6409 |   2.64 KB |
|        VerifyInterfaceProxyCall_Int |  master |    Core |   4.554 us | 0.0069 us | 0.0061 us | 0.7248 |   2.98 KB |
|        VerifyInterfaceProxyCall_Int |      PR |    Core |   3.333 us | 0.0038 us | 0.0034 us | 0.5569 |    2.3 KB |
| ----------------------------------- | ------- | ------- | ---------- | --------- | --------- | ------ | --------- |
|       VerifyInterfaceProxyCall_Void |  master |     Clr |   3.960 us | 0.0109 us | 0.0102 us | 0.7706 |   3.18 KB |
|       VerifyInterfaceProxyCall_Void |      PR |     Clr |   3.216 us | 0.0189 us | 0.0177 us | 0.6409 |   2.64 KB |
|       VerifyInterfaceProxyCall_Void |  master |    Core |   4.040 us | 0.0113 us | 0.0095 us | 0.6714 |   2.77 KB |
|       VerifyInterfaceProxyCall_Void |      PR |    Core |   3.193 us | 0.0135 us | 0.0126 us | 0.5569 |    2.3 KB |

I advice you to read changes commit by commit, so you would understand which exact optimizations I applied.

Happy review 🍷

@alexandrnikitin You might be interested as well.

P.S. I'm sane and fully admit that it makes negligible difference in real life, but I still like to see and realize that our tool is improved and fast 😊

@dtchepak
Copy link
Member

Thanks a lot for this @zvirja! I will try to work through this on the weekend.

I'm sane and fully admit that it makes negligible difference in real life, but I still like to see and realize that our tool is improved and fast

I can definitely appreciate this. I think this is a sign of an excellent craftsperson! They take care of the details that others won't always notice from the outside, but are still an important part of a job well done. 👍 😄

Copy link
Member

@dtchepak dtchepak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good @zvirja! Thanks!
Left some comments/questions, but happy for you to merge as is.

using System.Reflection;

namespace NSubstitute.Core
{
public class DefaultForType : IDefaultForType
{
private static readonly Dictionary<Type, object> DefaultPrimitiveValues = new Dictionary<Type, object>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be faster to inline this as a big switch/if/else if in DefaultInstanceOfValueType?

e.g.

if (returnType == typeof(bool)) return false;
else if (returnType == typeof(char)) return '\0\;
else if /* ... */
else Activator.CreateInstance(returnType);

Copy link
Contributor Author

@zvirja zvirja Apr 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback. Actually, I was also in doubt regarding this 🤔

I analyzed a few big repositories and found that not all the built-in value types are equally popular. The most popular ones appeared to be bool, int and long and double. Therefore, I left optimization only for those cases and decided to handle all the rest types as it was before. That's a reasonable trade-off IMO.

P.S. I benchmarked dictionary lookup with 3 elements vs if/else check for the worse case (3rd if is successful) and the latter appeared to be 4 times faster. So you were right! 🏅

@@ -46,7 +46,7 @@ private static void If(ICall call, Func<ICall, Predicate<EventInfo>> meetsThisSp
var matchingEvent = GetEvents(call, meetsThisSpecification).FirstOrDefault();
if (matchingEvent != null)
{
takeThisAction(matchingEvent.Name, call.GetArguments()[0]);
takeThisAction(matchingEvent.Name, call.GetOriginalArguments()[0]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add this commit comment to the code too? (see it doesn't get changed back in a later commit?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! Did it for all the non-obvious places around the PR.

Presume that most of times `Rules` is empty
and that rule configuration happens rarely, so
it's OK to use stack (which allocates on each push).
This object is constructed on each invocation,
so we optimize it to allocate less in normal case.
We are supposed to use `call.Arguments()` only
if we assume that arguments might be already mutated.
In this case it's not possible, as only by-ref args might be changed.
It appears that CallResults is not populated that often
and most of time it's just inspected. Current implementation
tries to gain from that pattern.
I've scanned a few repositories and found the most popular value types.
Optimize path for those cases.
Previous approach leaded to large amount of allocations
and worse performance. New approach allows to make
less allocations.
@zvirja
Copy link
Contributor Author

zvirja commented Apr 13, 2019

@dtchepak Followed up on your concerns. Given that I fully resolved all of them, decided to proceed with a merge, so I can start working on the PR nearby. Hope you are fine.

@zvirja zvirja merged commit 6aeaca3 into nsubstitute:master Apr 13, 2019
@zvirja zvirja deleted the improve-performance branch April 13, 2019 19:59
@dtchepak
Copy link
Member

@zvirja Of course, no need to re-review for a case like this. :)
Awesome work on getting this done!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants