Order by three custom fields (Y-m-d), then separate into yearly sections

A single field would be enough but if you manage your way with 3 that’s good as well.

I would do a loop through the results and end up with a structure like the following:

$years = array(
    '2015' => array(
        <WP_Post object>,
        <WP_Post object>,
        etc...
    ),
    '2016' => array(
        etc...
    )
);

Then you could go through this arrays again to build your html.

A lot of different technics can be used in that case. Just use your favorite I guess. I just think doing one database call is better than 3.

Not sure if I answer your question but I hope it helps.

EDIT:

Since you are ordering by date, you don’t need to re-order your result so it should be as easy as :

/* Your custom query */
$args = array(
    'cat' => 14,
    'post_type' => 'post',
    'posts_per_page' => -1,
    'orderby' => 'meta_value_num',
    'order' => 'DESC',
    'meta_key' => 'p2f_date'
);

$archive = new WP_Query( $args );

$current_year = null;


/* if our query has posts */
if( $archive->have_posts() ){

    /* loop through them */
    while( $archive->have_posts() ){
        $archive->the_post();

        $p2f_date = get_post_meta( get_the_id(), 'p2f_date' );

        /* Here I skip if the meta is not defined but you might want to handle that differently */
        if(empty($p2f_date)){
            continue;   
        }

        /* Get the year - Depending how you handle the post meta you might not need the index [0] */
        $year = date('Y', strtotime( $p2f_date[0] ));

        if( $current_year === null ){
            /* First item */
?>
           <ul class="year year--<?php echo $year; ?>">
<?php 
        }else if( $current_year !== $year ){
            /* New section */
        ?>
            </ul>
            <ul class="year year--<?php echo $year; ?>">
           <?php   
        }

        ?>
        <li>
            <a href="https://wordpress.stackexchange.com/questions/178156/<?php the_permalink(); ?>"><?php the_title(); ?></a>
        </li>
        <?php

        $current_year = $year;
    }

    ?>
    </ul>
    <?php

}

/* Reset your page data in case you need it after the custom query */
wp_reset_postdata();

Hope that makes sense and answers your question.
Here I used a custom WP_Query but you could just change the main query if this is relevant in your case.

tech