Viewport speed is usually limited by the CPU performance, since a lot of processing has to be done before the GPU can display the image.
Having large number of objects, or objects with complex constraints, can slow down the viewport because every object has to be evaluated every frame. So if you do have a huge scene, try to use layers to reduce the amount of visible geometry in the window.
Many animated modifiers have to to be evaluated every frame. One way to optimize these is to make sure the modifier stack is arranged in the most efficient way possible. This means armature animation comes before subdiv surface, for example.
Also, make use of OpenSubDiv when possible, it's a lot faster than the Blender subdiv code, especially when its
compute mode is set to GPU in User Preferences.
Simulations: Ocean Modifier, cloth sim, softbody, rigidbody and particles are all very demanding. It's best to bake these to their respective cache before attempting to playback the animation with Ctrl+A.
Simplify: Blender comes with tools to simplify the scene temporarily. One of them is the Simplify Panel in Scenes. Use it to reduce the complexity of your scene globally.
Also, make use of the
Maximum Draw Type setting in the object panel to simplify the display of background objects.
Display: Finally, we can reduce the complexity of the scene further by turning on
Only Render in the 3D viewport, this disables drawing of invisible objects and wireframes, which can speed up the viewport significantly in certain cases.
Some people reports increased performance when using matcaps, so you can try that as well, but your mileage may vary.
Ultimately, for a large production, it's not unusual for the file to be so heavy that playback at 24fps is not possible. If that's the case, you can use the OpenGL render feature in to render a quick preview movie to get a perfectly smooth playback.