the query performed on the homepage,
query_posts( 'posts_per_page=get_option( 'posts_per_page' ) and
$q = new WP_Query( 'posts_per_page=get_option( 'posts_per_page' )
should have the same exact performance with very very little to no difference between them as all of the above are exactly the same by default (that is, have the same query arguments by default). This is because all of these queries uses the
WP_Query class to run build and run db queries to return the queried posts.
get_posts() is a bit faster although it also uses
WP_Query. The big difference is that
WP_Query which legally breaks pagination.
What is true, the main query (primary query) runs on each and every page load according to the exact page being loaded. Removing the main loop and replacing it with a secondary query (either
get_posts()) is what is referred to as making the page slow. This is because you are doing the same work twice. As I said, the main query runs regardless, so you have already queried the db for posts, and replacing the main loop with a secondary query queries the db again.
Apart from issues created with pagination and other crappy issues, this is why one should never replace the main loop from the main query with a secondary one. I have done an extensive post on this which you can read here. To alter the main query, always use
pre_get_posts as it alters the query vars before the
WP_Query class builds and runs the SQL query. There are also other filters (post clause filters) available in
WP_Query which you can use to directly alter the SQL query
query_posts bad is that it alters the main query object on which many functions rely as
query_posts does the following
$wp_query = new WP_Query()
and it also alters the conditional tags. For a full explanation, you can read my answer here. A normal
WP_Query does not do that