I'm answering my own question. Perhaps it will be useful for someone else.

**Short answer**: You can short-circuit the page generation process via [wp_die()][1] or [wp_send_json()][2]. 

But since I don't want to send a 500 status it eliminates wp_die(), and since I want to send html not json, it eliminates wp_send_json(). So instead, I copied & modified the [wp_send_json() source][3] to produce the following:

    function wp_send_html( $response = null, $status_code = null ) {
        @header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) );
        if ( null !== $status_code ) {
            status_header( $status_code );
        }
        if ($response) {
            echo $response;	
        }
        die;
    };

In my code, after I determine the request is unchanged from the prior request, I call:

    wp_send_html(null, 304);

... which sends a 304 response including any html response headers that have setup, and an empty body.

In the single post places that I'm using this, I've decreased response times from about 4-5 secs, down to 1.0-1.3 seconds.

**Longer answer:**

From @Mark's post, the primary things I need to consider are the post last change, and the last change for any theme or other global element. I am only handling only single page/post pages, and I've added a `$MIN_UPD_DATE` global at the top of my functions.php with I'll update when I do global/style changes (yeah, I know that's a little kludgey).

My more complete code in my functions.php is as follows:

    // Update this on sitewide changes
    $MIN_UPD_DATE = DateTime::createFromFormat('M d Y H:i:s', 'Sep 01 2018 01:01:01');

	// For web pages and single post pages - note the last changed date
		// Thanks to: https://wordpress.stackexchange.com/questions/172966/if-modified-since-http-header
		// This will support clients sending HTTP header: If-Modified-Since 
	function handle_modified_since_header() {
		global $MIN_UPD_DATE;
		//Check if we are in a single post of any type (archive pages have no modified date)

		if( is_singular() ) { // excludes multi-post pages
			$post_id = get_queried_object_id();
			if( $post_id ) {
				header("Cache-Control: public");  
				// inherited/default was: Cache-Control: no-cache, must-revalidate, max-age=0
				
				$postModTime = new DateTime(get_the_modified_time('D, d M Y H:i:s', $post_id));
				if ($MIN_UPD_DATE > $postModTime) {
					$postModTime = $MIN_UPD_DATE;
				};
				header("Last-Modified: " . $postModTime->format("D, d M Y H:i:s") . " GMT" );
				return true;	// can use modified date to expire page
			}
		}
		return false;	// can NOT use modified date to expire page
	};

	// Checks single post & page entries for a Post Modified Date 
	// 	after the http request header: IF_MODIFIED_SINCE
	//     or after the MIN_UPD_DATE
	//  else, on any missing elements, assumes that request is expired.
	function is_request_expired() {
		global $MIN_UPD_DATE;
		$MOD_SINCE   = 'HTTP_IF_MODIFIED_SINCE';
		$DATE_FMT	 = 'D, d M Y H:i:s O';
		$postModTime = null;
		$isExpired   = true;
		$httpLastUpdate    = null;

		if (!isset( $_SERVER[$MOD_SINCE] )) {  // Quit on no http last mod date
			return $isExpired;
        };
        
		$httpLastUpdate = DateTime::createFromFormat($DATE_FMT, $_SERVER[$MOD_SINCE]);
		if (!$httpLastUpdate) {       // Quit on can't decode last mod date
			return $isExpired;
		}

		$post_id = get_queried_object_id(); // Get Post last modified date
		if( $post_id ) {
			$postModTime = new DateTime(get_the_modified_time('D, d M Y H:i:s', $post_id));
		};

		if ($postModTime) {
			// http last-mod-date is before post-list-mod-date or before min-upd-date
			$isExpired = ($httpLastUpdate < $postModTime) || ($httpLastUpdate < $MIN_UPD_DATE);
		};
		return $isExpired;
	}

	function send_on_not_expired_single( $wp_query ) {		
		if ($_SERVER['REQUEST_METHOD']!=='GET') {  // ignore for non-GET requests
			return $wp_query;
		}

		$use_modified_since_rule = handle_modified_since_header(); // conditionally set Last-Modified http header
		if ($use_modified_since_rule) {
			$is_expired = is_request_expired();
			if (!$is_expired) {
				wp_send_html(null, 304);
			};
		};
		return $wp_query;
	}
	add_filter( 'parse_query', 'send_on_not_expired_single', 200);

	/**
	 * Send an HTML response back to an html request.
	 * 				patterned on the v4.9.8 WP codex wp_save_json
     *              from: https://core.trac.wordpress.org/browser/tags/4.9.8/src/wp-includes/functions.php#L3179
	 *
	 * @param mixed $response    Variable (usually an array or object),
	 *                           then print and die.
	 * @param int   $status_code The HTTP status code to output.
	 */
    function wp_send_html( $response = null, $status_code = null ) {
        @header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) );
        if ( null !== $status_code ) {
            status_header( $status_code );
        }
        if ($response) {
            echo wp_json_encode( $response );	
        }
        die;
    };

As a final note, per some comments/suggestions, I did review some caching plug-in's. Although some of them do have very good 'last-modifed-date' checking/updating logic (which I don't), the ones that I reviewed either specifically exclude logged in users from caching, or have significant limitations for logged in users which eliminate their usefulness for my focus.

  [1]: https://codex.wordpress.org/Function_Reference/wp_die
  [2]: https://developer.wordpress.org/reference/functions/wp_send_json/
  [3]: https://core.trac.wordpress.org/browser/tags/4.9.8/src/wp-includes/functions.php#L3179