Cache Get_posts

You can use transient cache to cache your custom query. Here is a simple code to set_transient cache for your query for 12 hours. In 12 hours WordPress will not make new query but fetch posts from transient. On expiration, it will save new query in transient again for next 12 hours.

I have been using transient for many queries on my website but make sure you set expiration time carefully otherwise your new post might not appear on your website for some time.

<?php 

    // Check for transient. If none, then execute Query
    if ( false === ( $postslist = get_transient( 'postslist_query' ) ) ) {

        $args = array(
            'numberposts' => 10,
            'orderby'       => 'meta_value_num',
            'order'         => 'DESC',
            'meta_key'      => 'event_date',
            'meta_query'    => array(
                array(
                    'key' => 'event_date',
                    'value' => $today,
                    'compare' => '<=',
                    'type' => 'NUMERIC'
                )
            )
        );

        $postslist = get_posts( $args );

      // Put the results in a transient. Expire after 12 hours.
      set_transient( 'postslist_query', $postslist, 12 * 60 * 60 );

    }

    foreach ( $postslist as $post ) :  setup_postdata( $post );

?>

read more on Transient API