SDK Agent Creation Performance
Some SDK users express concern for the overhead of managing
Agent objects for bulk file encryption.
Agent is a lightweight object, and the most resource-consuming operation in the default initialization case is retrieving profiles from an OS persistor or the disk. However, the agent can be initialized without profiles, and a cached profile object can be added to it making it an extremely lightweight part of the encryption process. Here we’ll analyze the performance of several different scenarios.
First off, we set up a single
ISAgent object outside of our testing loop.
ISAgent agent; agent.initialize();
Then establish a baseline for some simple non-networked method call.
10,000 iterations of this took 0.122 seconds
Next, create a new
ISAgent object and initialize it without profiles, then add a profile that was stored in a variable earlier.
ISAgent a2; a2.initializeWithoutProfiles(); a2.addProfile(profile, true); a2.getDeviceProfileForKeyId("notakey");
10,000 iterations took 2.45 seconds.
The next step is to create a new
ISAgent and fully initialize it with the default persistor (the Windows DPAPI persistor for this test).
ISAgent a3; a3.initialize(); a3.getDeviceProfileForKeyId("notakey");
10,000 iterations took 70.7 seconds.
The final scenario sets up a persistor that uses an encrypted file to compare disk access.
ISAgent a4; a4.initialize(passwordpersistor); a4.getDeviceProfileForKeyId("notakey");
10,000 iterations took 105 seconds.
Comparison with network usage
Now let’s consider each of these scenarios in the context of a network call.
First, the single-agent case with a getKey call for a key we previously created.
10,000 iterations took 345 seconds
Creating a new agent without profiles and adding a cached profile.
ISAgent a2; a2.initializeWithoutProfiles(); a2.addProfile(profile, true); a2.getKey(keyId, getResponse);
10,000 iterations took 336 seconds.
A new agent using the default OS persistor.
ISAgent a3; a3.initialize(); a3.getKey(keyId, getResponse);
10,000 iterations took 401 seconds.
Using the file-based password persistor.
ISAgent a4; a4.initialize(passwordPersistor); a4.getKey(keyId, getResponse);
10,000 iterations took 425 seconds.
For the sake of completeness, each case was tried again with each respective agent being used to initialize an instance of
ISChunkCryptoCipherAuto that was then used to decrypt a short string encrypted earlier.
ISChunkCryptoCipherAuto c(agent); std::string recovered; c.decrypt(cipher, recovered);
The results for the same 10,000 iterations:
- Single ISAgent – 321 seconds
- New ISAgent with cached profile – 344 seconds
- New ISAgent with default persistor – 365 seconds
- New ISAgent with password persistor – 389 seconds
You can see that a call to the operating system to retrieve profile data is a reasonably expensive operation even when compared to a network call. In a case where the application architecture does not permit storing the
ISAgent object, you can cheaply create a new agent. The
ISAgentDeviceProfile object is a simple data holder, and it can be created from strings so long as that data is protected.
The results so far were generated in debug mode. As the results below from a program build in release mode with compiler optimization show, the cost of creating a new agent is even lower in practice. Results may vary slightly based on hardware, but network speed is more or less constant.
You can find the source code in the Ionic Github samples repository under AgentAnalysis.cpp. Included is a modified test program that runs all these test cases together and produces prettier output. The results of running that test are included
Windows x64 Release build:
9999 tests find getKey chunk Single agent: 0.000 157.391 166.101 Cached profile: 0.084 167.897 173.226 OS persistor: 12.544 178.862 179.063 Password file: 46.457 225.098 224.128