It’s more about the total area of texture your entire project uses (no matter if it’s split in many small textures or a few large ones). Also, textures with high dynamic range like HDRIs and 32-bit displacement maps are much heavier. But really, textures are not a problem for performance until your scene gets past the amount of Vram on you gpu (look at the memory stat in the top bar when rendering). Only then would you need to start reducing and optimizing textures.
Linked duplicates, also called instances, are really useful for creating heavy scenes (I have managed scenes in the billions of triangles with barely any render time hit). If you try subdividing the default cube until it’s over a million triangles and then duplicate it with shift+d, that will kill your performance really quick. But if you duplicate it with alt+d, each new instance will have a small impact and you will be able to have possibly hundreds on screen. That’s because the mesh is stored in memory only once. I must add that this memory saving effect only works if the instances have no modifier on them, as modifiers force each instance to be different from each other and they will be stored individually.
An other tip: when instancing lots of really heavy objects, set their viewport display to bounding box, so the object doesn’t have to be fully displayed in the viewport, this will allow better viewport performance.
Cycles will have no problem displaying everything, because ray tracers handle polygons really well. The thing they fear is complex materials and lighting, especially if they take a large part of the screen.
Particle systems also use instances and have the advantage that you can turn them off in the viewport to save on performance, so they are perfect for large grass fields or tree leaves.
I have a tip that helps with denoising. Well, maybe not so much for the render time, but the quality level will be better.
1-Render your scene at 200% resolution. You can reduce your samples so it takes the same amount of time it did previously.
2-Denoise and save the image, still at double resolution. This will allow the denoiser to better capture the fine details and textures. Also, I recommend using open image denoise for a final render. Set its prefiltering to “none” if it works in your scene, but if there is still noise set it to accurate.
3-Open the saved render in an image or video editing software and reduce it back to its intended resolution. The image will look more crisp and detailed than it would normally have. I don’t recommend doing that step with Blender, as it has pretty bad filtering when rescaling an image.
In Blender 3.2, a new feature is getting included which allows individually chosen transparent objects to cast caustics. This will allow for more realistic and less noisy underwater scenes.
In the future, the devs have mentionned eventually adding path guiding. This will allow light rays to be sent in directions that are more useful and clear noise faster (if I understand correctly).