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
Discussion: speed comparison against Mockito #13
Comments
Why are you sure it's mockito is adding slow down? Anyway, I'll try to benchmark it against mockito. It won't probably be very fast. The problem here is that the speed is very dependent on use-case.
Class re-transformation definitely adds slow down, but it's cached. |
99% sure😁 Bytecode transformations + reflection at runtime is very resource consuming. unfortunately, the project is not public. There are 3k+ tests and most of them are heavily use Mockito. As a result tests execution time ~3min on my machine, which is very long time for unit-tests. |
From my (limited) experience, mockk seems to be slow. A single unit tests with one RelaxedMock, one method stub and one verification took more than 1 second to execute with Java 8. |
That may be the case. Did you have a chance to compare it to mockito? Im
interested to make it faster.
Definitely class transformation what makes it slow. If you reuse classes,
then it caches it and shouldnt be so slow for subsequent calls. Of course
something probably can be improved even now.
I understand your case is simple. But anyway if you can share your case as
github project I can exactly reproduce it and iterate on performance
optimiaztion as far as I can go
29 янв. 2018 г. 2:24 PM пользователь "Jonas von Andrian" <
notifications@github.com> написал:
… From my (limited) experience, mockk seems to be slow. A single unit tests
with one RelaxedMock, one method stub and one verification took more than 1
second to execute with Java 8.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#13 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AFAbjBl04x7Or67e-IbCQmWVkisYjJtuks5tPcaegaJpZM4RMlcP>
.
|
@johnny I cannot reproduce such slow mocking. RelaxedMock works for me about 2-4ms. Are you sure that you do not measure the first mokk invocation? Because for the first invocation JVM loads classloader, and it's pretty big one in case of mokk (requires 1-1.5 second) but it's the single operation, so +1 second for all your tests instead +1 second on each mock or relaxed mock |
No additional information provided, so no possibility to improve. Will get back to it if use case appear |
Hello @oleksiyp! I pulled a test for a component I recently wrote into a public repo: https://github.com/ratiofu/Utf8StreamReader - compare the startup and run time of |
@oleksiyp, attached please find the trace (the Github project I shared above is updated accordingly). Longest delay is here:
|
Yep looks like ByteBuddy installation takes a lot of time. You can try
running agent in command line if you would like to compare. Anyway worth to
check ByteBuddy issues if there is some bug that cause delay under some
conditions. I have no access to PC during day, so will get back to the
issue in the evening/weekends. Thanks
9 марта 2018 г. 8:49 AM пользователь "Thomas Jung" <notifications@github.com>
написал:
… @oleksiyp <https://github.com/oleksiyp>, attached please find the trace
(the Github project I shared above is updated accordingly). Longest delay
is here:
615 [main] TRACE io.mockk.impl.JvmMockKGateway - Starting Java MockK
implementation. Java version = 1.8.0_152.
5980 [main] TRACE io.mockk.proxy.MockKInstrumentation - Byte buddy agent
installed
(logger is configured to show time since start)
mockk-trace.log
<https://github.com/oleksiyp/mockk/files/1795985/mockk-trace.log>
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#13 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AFAbjBXXal-5lEtZ7oa1YvICyqo0iuJTks5tcjQFgaJpZM4RMlcP>
.
|
@raphw can you sugest why ByteBuddy can be slow at startup? |
The installation is a very expensive operation in itself, unfortunately. It often requires a stop the world with full garbage collection. It is especially expensive since Java 9 where Byte Buddy needs to do some extra tricks to get around the restriction. I'd recommend to profile a run and see what takes the most time, it is very machine dependant but I can have a look if something is pointed out. The installation relies a bit on being aone time operation, it does therefore work best for running big suits rather then single tests. It is also more costly on Windows machines, by my experience. To avoid the expensive start up, you can add byte-buddy-agent as a JVM javaagent pameter. Byte Buddy will detect thatt and not run the expensive routine. |
Just some general feedback: advice instances are immutable and very expensive. Dont create them for each transformation. Your synchronized maps will also slow things down significantly. |
Thanks!
9 марта 2018 г. 2:31 PM пользователь "Rafael Winterhalter" <
notifications@github.com> написал:
… Just some general feedback: advice instances are immutable and very
expensive. Dont create them for each transformation. Your synchronized maps
will also slow things down significantly.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#13 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AFAbjHx_kLfx3AqEyNKAczovqj7iUl7Tks5tcoQ5gaJpZM4RMlcP>
.
|
Unfortunately, mockk is significantly slower than mockito when creating mocks:
Why mockk is so much slower? |
More measurements for hot-cases only (first mock creation is not measured): |
@angryziber what is JDK used for measurements? |
I think there is a lot of things to consider regarding how to do benchmark itself. Can you share some code snippet to make my life easier? Mockk creates subclasses together with inlining to make abstract methods mockable. I wonder if that can be the reason of slow down. Another trick is that for instantiation second subclass is created(subclass itself is cached, but one-time subclass creation is performed). Do not remember all the details on why it is needed. I think this can have more fine-grained control when it is applied. |
Following code:
Gives me following answer:
|
If you subclass and retransform, you can pretty much sum up up the numbers for both on the Mockito side and that seems right, too. Retransformation is expensive as it discards a lot of optimizer work, there is not much to improve here. |
@raphw my only point is that subclassing not always needed and can be skipped depending if a class has abstract methods or not. |
I'll share my measurements as well, although unfortunately they're not very structured. I've converted a small project, roughly 100 tests across 6 modules, from Mockito to Mockk. I then ran Gradle Profiler with default options and test tasks set to never be up to date, to ensure that it's almost exclusively tests run that's measured. Here are the results for Mockito, with mock-maker-inline applied where required (durations are in milliseconds): And here are for Mockk: In the first Mockito version I have mock-maker-inline applied to 4/6 modules, but if I apply it to all of them (including a module with 25% of all tests), the results are a bit closer: Unfortunately I'm not able to investigate more and give more specific profiling results, but it does seem like general byte-buddy performance issue. Thing is, with Mockito I can at least disable mock-maker-inline in modules where I don't need it, and overall I'd say not needing it is (or should be) the norm, not the exception. I'd love to have an option to opt-in to more expensive mocking on a per-case basis, when it happens that I'm mocking an external class. Another thing people sometimes do is they ditch mock-maker-inline in favor of all-open compiler plugin. For some this might be preferable, since it's a small one-time cost, although afaik works only for own classes. |
Thanks for comparation. Mockk will lost most of features without inlining |
Instrumentation is a very expensive feature. Byte Buddy needs to jump through some hoops to get it done. You can try to add byte-buddy-agent as a Java agent on the command line if you want to avoid the majority of costs. The good news anyways is thst the costs are one-time. In big test suits, the costs amortize. |
I'm more concerned about running individual tests or test classes from IDE during normal development. Adding javaagent:byte-buddy-agent.jar does help, but still, statistics for a small test class with 12 tests, 2 mocks (just one of final class):
Mockk is still two times slower. And in this case the costs unfortunately don't amortize, because such test classes are run in isolation many times. I'm not trying to argue which library is better or whether the speed is acceptable, just sharing my stats and thoughts. In any case some automated benchmark and comparison between Mockito, Mockito with mock-maker-inline and Mockk would help track performance and make informed decision about mocking library. I personally love the API, but to me the speed is a dealbreaker. |
It would be interesting to attach a profiler to both Mockk and the Mockito runs to see what the time is spent on. |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. If you are sure that this issue is important and should not be marked as |
What about putting an important label as not suggests?
…On Wed, Aug 28, 2019, 21:23 stale[bot] ***@***.***> wrote:
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions. If you are sure that this issue is important and
should not be marked as stale just ask to put an important label.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#13?email_source=notifications&email_token=AAGXKYGSCQG6QAEPM4VCH6TQG27CDA5CNFSM4EJSK4H2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD5MBALQ#issuecomment-525865006>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAGXKYEZMFOFD27F3YF4QGDQG27CDANCNFSM4EJSK4HQ>
.
|
@raphw btw this initialization time depends on some conditions, right? Alike is it JVM, JDK. Which VM version is used. Which OS is used. Wont it be nice to have benchmark and suggest some particular version in docs. |
Of course, newer Java versions tend to be faster as they do more initialization lazier. Generally, absolute benchmark numbers are seldom meaningful, just comparison numbers of the same run; therefore it is often good to also add a no-op benchmark as a baseline. |
Any work going on to improve performance? |
I spent some time preparing the JMH testing rig for mockk (#763). I did profile a simple case of creating a mock and stubbing single method and here you can find Java Flight Recorder profiling results: 92c448c#diff-6cd22ac6a6f83bf3ea8124c66fd49373dcd1f28bad5e30354d5fc13c880fdf36. Two observations (all % below are relative to "total time" (it's sampling profiler so it's not really time)):
every { mockedClass.mockedFun() } returns "Hello, mockk!" seems to take up more time (58%) than actually creating a mock(41%) with val mockedClass: MockedClass = mockk()
Could we drop using ConcurentHashMap in favour of non-thread-safe implementation? At least in my case mockk is not used that often in a multi-threaded scenarios. Maybe separate mode could be implemented? |
Hi, @tmszdmsk thanks for the work done on benchmarking MockK. In my opinion, there are few things:
Few questions:
Again thank you for your contribution, just want us to get to all roots regarding these questions, so happy to drill down and assist. |
You can reduce the initialization time by a lot by:
|
Hi @oleksiyp, @raphw, thanks for your replies.
Yes, I can imagine. I was thinking about additional mode when creating mocks (like
that's possible, but this is based on the data I collected, you can easily downlaod the JFR(Java Flight Recorder) file and review it yourself, IntelliJ has native support for .jfr files.
I don't think this skews the results that much. I think JMH takes care of those one-off events by running "warmup" iterations before the actual benchmark where it warms the JVM.
It's not
JFR doesn't provide such numbers as it is sampling profiler (checking the status of the JVM every X ms and producing the results based on that). Hard to tell. From my experience with property-based tests (which cause many iterations of the same test to be executed) it's @raphw, it is possible to attach the agent in the benchmark execution (as it is simple jar file), but I didn't have time to do it. My hypothesis based on above is that it won't make much (if any) difference. I think good next step would be to actually quickly "hack" using different implementation of the map just to run those tests again and compare the results. I may find some time to do this sometime this week, but maybe someone could look into it earlier (and has more experience with mockk codebase). I am happy to help if anyone needs it. |
Do you have any resources that would help me implement such workarounds? |
Here is a reference: https://stackoverflow.com/questions/50498102/how-to-set-jdk-attach-allowattachself-true-globally You can also find byte-buddy-agent on Maven Central and add that jar via -javaagent: to your test VM. |
There's a couple of small updates on improving MockK performance BenchmarkingThere are now basic benchmark tests in the project #904 (credit to @tmszdmsk for providing them). They're a good baseline. They show that it still the case that creating a stub is expensive , even if the stub is not used. Here's a graph comparing no stubbing or mocking (which is obviously very fast), only mocking (which is of course slower), and mocking and creating a stub (which, as noted previously, is significantly slower) It would be a huge benefit to improve the benchmarks. Here's what I'd like to see:
Help would be really appreciated! Dropping
|
if (cls.isValue) { |
MockK + KSP
I've been thinking about updating MockK to take advantage of the new Kotlin kapt replacement: KSP
Other Kotlin mocking frameworks (https://github.com/bitPogo/kmock, https://github.com/mockative/mockative) use KSP to replace reflection, and they can do this in multiplatform projects. The drawback is that both libraries only mock non-sealed interfaces - they don't create 'random' instances of objects (presumably for multiplatform compatibility reasons).
I wonder if MockK can use a hybrid approach, and use KSP to introspect the source code during compilation, save the results to disk, and then during runtime instead of using reflection (which is expensive), determine which 'random' object to create using the saved data.
I think this 'compile time reflection' approach is similar to what https://github.com/JetBrains-Research/reflekt aims to do, but progress on that project seems slow, and has restrictions on Kotlin versions.
I just did a quick google search, how about [this])(https://github.com/boswelja/kotlinx-benchmark-table-action) to integrate kotlinx-benchmark in a GHA? Also, KSP looks really interesting, and I agree we could take a hybrid approach by doing symbol processing at compile time and then using the result only if needed. It would basically be moving some complexity (and time) from runtime to compile time, but I think it's worth looking into. |
I'm using Mockito on my current project. Turns out that it's very slow. Would be great to see some benchmarks.
The text was updated successfully, but these errors were encountered: