Remember that StateT (and by extension, State) is just a newtype wrapper. Newtypes have no runtime cost. In this case it simply contains a
function. Constructing an expression full of binds against StateT before supplying an argument via runStateT is no different than combining several functions and supplying their argument later.
As far as memory usage is concerned, there is one caveat. The state variable that is returned after every bind can build up thunks if it is altered but not fully evaluated in any given step. This could lead to a memory link as operations are tacked onto the state variable you supplied. If you use Control.Monad.State.Strict, that variable will be evaluated whether you use it or not, preventing laziness from building up thunks.