I noticed that in write-back caches, when the cpu wants to write to a block, it fetches the block from memory and then updates it. If the block is going to be overwritten and changed by processor, why does the cpu need to read that block from memory?
While it would be possible to construct a 128-bit wide memory bus with a separate write-enable control for each octet (sixteen in all), doing so would generally be more expensive than designing the bus to require that any operation which writes to any byte in a 128-bit cache line must supply data for all sixteen. Although there are times when being able to update some bytes in a line without having to read the others would yield a performance advantage, in many cases the penalty of having to fill a cache line from memory sometime between when the fist write gets posted and when the line gets flushed will be minimal, and the benefits of eliminating that penalty may be insufficient to justify the cost of the extra write-control logic.
There are several issues involved in this design decision. Since conventional DRAM does not support finer-grained write enable (graphics memories often do), when the data was eventually written back if an entire memory access chunk (often cache block sized) was not written then a read would be necessary. Since conventional DRAM interfaces have significant read-write turnaround latency (and the DRAM row is kept open), performing a read-merge-write at cache block writeback can reduce memory throughput.
Even with finer-grained write enable, cache blocks would have to track validity at the granularity of writes (assuming write allocate). This would add some overhead in cache metadata (e.g., 64-bits for a straightforward byte granular validity tracking of a 64-byte cache block, potentially nearly tripling last level cache metadata — a 4 MiB 16-way cache for a 40-bit address space would have 22 tag bits per block and a few bits for coherence).
The common case is assumed to be the presence of spatial locality and non-complete overwriting. I.e., other data in the written-to cache block will be read before it is written back. Streamed writes are a somewhat frequently occuring case where complete cache blocks are overwritten, but such are also often non-temporal writes that one would prefer not be allocated to the cache. While determination of non-temporal write streams is usually pushed to software, because of the temporal proximity of such writes it would not be extremely difficult for hardware to manage them. (Some x86 string operation implementations do exploit complete overwriting of cache blocks.)
Some instruction sets provide cache block zeroing and/or cache block allocating instructions which allow software to avoid reading memory when entire cache blocks are overwritten (or will have "don't care" data values). Such could be useful for smaller streaming writes (where the buffering benefit from cache allocation is greater than the cache pollution penalty or where reuse while in cache is more likely) and memory allocation (where hardware zero initialization would be a benefit beyond the avoidance of read for ownership).
Having per-byte validity for cache block contents would also complicate cache coherence and consistency. With block-granular data presence, a read coherence request need only specify the cache block. Even if only a single caching agent can hold a partially dirty block (similar to the single owner model for cache coherence), multiple caches might need to provide the data for a request as clean bytes might be resident in another cache with the dirty bytes invalidated.
(Finer-grained validity tracking can have an additional benefit in reducing false sharing.)
Another waste of bandwidth in typical caches is the writeback of modified memory with dead values. For example, when a stack frame is freed, the values are dead but hardware does not know that the modified cache blocks have no modified live values and so it must writeback such blocks (writebacks to memory would only happen if the extent of the stack was smaller for a long enough period). Similarly, when a program ends it will typically leave a lot of modified dead values in cache. Such are not common occurances, but they demonstrate that there is potential for improvement.