you can use the below function to reduce the number of virtual rows by considering only the years in between.You can check the SQLFIDDLE to check the performance.
First consider only the number of days between start date and the year end of that year or End date if it is in same year
Then consider the years in between from next year of start date to the year before the end date year
Finally consider the number of days from start of end date year to end date
Hence instead of iterating for all the days between start date and end date we need to iterate only the years
WITH all_dates AS (SELECT (TO_CHAR(START_DATE,'yyyy') + LEVEL - 1) YEARS_BETWEEN,start_date,end_date FROM (SELECT TO_DATE ('21/03/2011', 'DD/MM/YYYY') AS start_date , TO_DATE ('25/06/2013', 'DD/MM/YYYY') AS end_date FROM dual ) CONNECT BY LEVEL <= (TO_CHAR(end_date,'yyyy')) - (TO_CHAR(start_date,'yyyy')-1) ) SELECT DECODE(TO_CHAR(END_DATE,'yyyy'),YEARS_BETWEEN,END_DATE ,to_date('31-12-'||years_between,'dd-mm-yyyy')) - DECODE(TO_CHAR(START_DATE,'yyyy'),YEARS_BETWEEN,START_DATE ,to_date('01-01-'||years_between,'dd-mm-yyyy'))+1,years_between FROM ALL_DATES;