While profiling a views table, berdir showed me that we actually create a new display object for every field. Since the only state required to build the object is bundle, field, and display options, it seems that we can maintain a cache for them and reduce the number we need to create.
Attached patch adds this, and the results look promising.

The part of this patch that removes bits on the views field plugin is due to the fact that having the view in the display options makes serializing it for a cache key prohibitively expensive. Also, berdir assured me that these are actually useless and cannot possibly be used by the formatter.
Comments
Comment #1
yched commentedSo, yes, the perf of "field-by-field" Views, currently based on individual $item->view() calls, is crazy poor. We've had a long standing plan to make it better, it just never got to the top of the todo lists so far :-). The issue is #1867518: Leverage entityDisplay to provide fast rendering for fields.
Basically, the idea is a View should :
- run its query,
- load the corresponding entities,
- create a runtime EntityViewDisplay object, and configure it with ->setComponent() on each of the N fields it needs to display. This happens only once.
- render the entities in one pass: $output = EVB::buildMultiple($entities)
- for each row/entity and each field in the row, pick the output of the field in the $output array: $field_output = $output[$entity_id][$field_name]
(in practice, this is a bit more complicated because of views relationships and stuff like that, but that's the idea)
I still think this is the right approach (instanciates the formatters only once for all entities, benefits on multiple rendering), but this does not exclude the optimizations done in the patch here, that will benefit other (non-Views) cases that need to call $item->view() repeatedly on the same field with different values.
Comment #2
yched commentedActual review now (nitpicks)
- The $viewDisplays member should be documented. Also, given that it only contains the custom runtime Displays created for the case of "->viewField(explicit display settings)", maybe it could be more accurately named customDisplays ?
- For code organization / symmetry, I'd favor a protected helper for the whole "figure out a $display for those $field_name and display_options" part of viewField(), that internally takes care of "is $display_options a $view_mode or an array of explicit settings".
- The piece of comment about the internal cache of displays currently in the phpdoc for the the helper would move inside the "if (array of explicit settings)" part of the code ?
Comment #3
yched commentedRefactored as per #2.
+ renamed the property so that it's clearer that those persisted displays are only used for the case of "single field display" (which is not the main use case for EntityViewBuilder)
Comment #4
yched commentedSide note - @msonnabaum : #2221577: Fix assumption that field settings is not a nested array also had to serialize+hash a settings array to use as a static cache key.
Over there, this was done with Crypt::hashBase64(serialize($settings)).
Patch here does crc32(serialize($display_options)
Should we unify ?
Comment #5
amateescu commented@see should go below @param.. I think.
As you argued in a different issue, some other subsystem could use the word Display, so maybe EntityDisplay would be better here. Or EntityViewDisplay? Also, 80 chars :)
How can this be a string when we type hint it as an array in the method signature? :P
Same as above.
Missing a full stop.
We call this variable $entity_type_id these days.
All entity classes can use the ::create() static method now.
Comment #7
yched commentedThanks @amateescu :-)
1. In function phpdocs, yes, but this looks really weird in class members phpdocs ? Left it untouched for now.
2. Fixed
3. Fixed. Forgot to change the typehint for @msonnabaum's version, which was only about "array of custom settings"; Should hopefully fix the fails ?
4. The typehint was wrong, but that comment is correct : this is the part of the code that deals with "$display_options passed as an array".
5. Fixed
6. Fixed
7. This is just moved around, but sure.
Comment #8
yched commentedNo real reason for that $view_mode variable.
Comment #9
amateescu commentedPending an answer to #4, this looks ready to me.
Comment #10
msonnabaum commentedI used crc32 because it's the fastest way to hash something for non-cryptographic purposes. It has a higher collision rate, but for a static cache with other strings attached, the risk of that seems extremely low.
Using sha256, base64-ing, then doing a string replace as Crypt::hashBase64 does, is totally overkill.
The difference is perhaps a micro-optimization, but I think it's generally a bad habit to not distinguish between cryptographic and non/cryptographic hash requirements.
Comment #11
yched commentedThanks @msonnabaum. I'll report that back in #2221577: Fix assumption that field settings is not a nested array
Then, RTBC, as per #9 and #10 ?
Comment #12
amateescu commentedYep, sorry I forgot to do it myself :/
Comment #13
alexpottYeah I only used sha256 in #2221577: Fix assumption that field settings is not a nested array to not having any discussion about hashing. I completely agree with the statement
This issue is a major task that will improve performance significantly and the disruption it introduces is limited. Per #2350615: [policy, no patch] What changes can be accepted during the Drupal 8 beta phase?, this is a good change to complete during the Drupal 8 beta phase. Committed fa1b5ba and pushed to 8.0.x. Thanks!
Comment #15
yched commentedFYI : changed Crypt::hashBase64() to crc32() in TDM::getPropertyInstance() in the cleanup patch in #2364267: Clarify the logic in TypedDataManager::getPropertyInstance()