As Eric Gerlitz notes in his answer, a lot of things are happening under the hood of the Umbraco installation. In a nutshell, 400 nodes should not cause too many problems as they are published as XML and then cached. In addition, the standard Umbraco installation is not a hungry resource, so there is something else in the game, and probably quite thoroughly. Therefore, check the following:
How do you access node content? The most basic mistake you can make is to use the Umbraco API to access node content when you don't need it. For read-only scripts where you only need published data (for example, displaying content on a published website), you should use methods that request published data in an XML cache. In 4.11.x, it will be umbraco.NodeFactory.Node , INode or DynamicNode using the DynamicNodeContext model DynamicNodeContext to the macro. You should avoid using Document , Content , etc. objects since they make calls to the database.
How do you access media? . Starting from 4.8, all media stored in the CMS are indexed in the same way as nodes. In 4.7, you should use new Media(id) to retrieve the file in the library. This gets into the database and is therefore very expensive on request. You should use new DynamicMedia(id) as this extracts file information from the index and is very fast and makes a significant difference.
Do you cache macros? Careful use of this feature can help en masse. Even XML cache calls have a trace, and things like rendering basic site navigation can be quite expensive. I tend to cache complex navigation macros like these that are repeated throughout the site. Yes, there will still be a strike at the first request, but subsequently it will not. However, if you have limited resources, do not use macro-cached nuts. Be selective and consider which pages get the most traffic and which have more complex node bypass requests.
What data do you save in your document types? You don’t really need to check this out, but it’s worth checking out, especially if you realize that your site is growing in size. If you use a multi-node picker, do you store XML or CSV? CSV is significantly smaller because it only stores node identifiers. XML storage is useful for structured data and for access to using XSLT, but redundant if you only pull out the identifier of the media or content nodes. Do you also have additional fields that are not used? They will be published in XML and can save you resources as XML grows. Additional fields also mean more trips to the database when nodes are saved and published. So it’s better.
Do you save data that should not be? There is a tendency to make everything CMS-editable. LinkedIn URL, Twitter ID, company phone number, payment gateway callback pages, etc. But in reality, such things do not change very often, if at all. They can be safely assigned to keys in the AppSettings module of the web.config file. And then access through the static class "ConfigConstants", which makes the keys read-only. This saves a bit of space in the XML cache and makes it easy to load save and publish pages. It also means that you do not need to query the XML cache for data.
Do you have an intermediate request method and / or extension? This is by no means necessary, but I like to have extension methods that prevent reuse of code through the user interface. This means that I can always be sure that when I request a multimedia element, the boolean property, root node, etc. I use the same code every time. At this point, I can also do a little caching. Therefore, if I have a "Site Settings" node, I can cache its properties in a custom lightweight object, so I did not need to request a "Site Settings" node, and then its properties every time I needed to access the data on the site such as address, phone number, URL, etc.
Hope this helps a bit.