diff --git a/src/wp-includes/user.php b/src/wp-includes/user.php
index ca4b3d03fd..713f6d7124 100644
--- a/src/wp-includes/user.php
+++ b/src/wp-includes/user.php
@@ -536,6 +536,85 @@ function wp_validate_logged_in_cookie( $user_id ) {
 	return wp_validate_auth_cookie( $_COOKIE[ LOGGED_IN_COOKIE ], 'logged_in' );
 }
 
+/**
+ * Counts the number of posts for a particular post type.
+ *
+ * @param  int	  $userid      User ID.
+ * @param  string $post_type   Optional. Single post type or array of post types to count the number of posts for. Default 'post'.
+ * @param  bool   $public_only Optional. Whether to only return counts for public posts. Default false.
+ * @return int Number of posts the user has written in this post type.
+ */
+function count_user_posts_for_single_type( $userid, $post_type = 'post', $public_only = false ) {
+	global $wpdb;
+
+	$cache_key   = "count_user_{$post_type}_{$userid}";
+	$cache_group = $public_only ? 'user_posts_count_public' : 'user_posts_count';
+
+	$count = wp_cache_get( $cache_key, $cache_group );
+
+	if( false === $count ) {
+		$where = get_posts_by_author_sql( $post_type, true, $userid, $public_only );
+		$count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts $where" );
+
+		wp_cache_add( $cache_key, $count, $cache_group );
+	}
+
+	return absint( $count );
+}
+
+/**
+ * Clears the cached count for user for given post type.
+ *
+ * @param int 	 $userid    User ID.
+ * @param string $post_type Post type.
+ */
+function clear_count_user_posts_cache( $userid, $post_type ) {
+	$cache_key   = "count_user_{$post_type}_{$userid}";
+	$cache_groups = [
+		'user_posts_count_public',
+		'user_posts_count',
+	];
+
+	foreach( $cache_groups as $cache_group ) {
+		wp_cache_delete( $cache_key, $cache_group );
+	}
+}
+
+/**
+ * Check if post author has changed. If changed, clear count posts cache for user.
+ *
+ * @param int     $post_ID      Post ID.
+ * @param WP_Post $post_after   Post object following the update.
+ * @param WP_Post $post_before  Post object before the update.
+ */
+function check_author_change( $post_id, $post_after, $post_before ) {
+	if ( $post_after->post_author !== $post_before->post_author ) {
+		$post_type = get_post_type( $post_id );
+
+		clear_count_user_posts_cache( $post_after->post_author, $post_type );
+		clear_count_user_posts_cache( $post_before->post_author, $post_type );
+	}
+}
+add_action( 'post_updated', 'check_author_change', 10, 3 );
+
+/**
+ * Counts the number of posts for given post types.
+ *
+ * @param  int	  $userid      User ID.
+ * @param  array  $post_types  Optional. Single post type or array of post types to count the number of posts for. Default 'post'.
+ * @param  bool   $public_only Optional. Whether to only return counts for public posts. Default false.
+ * @return int Number of posts the user has written in this post type.
+ */
+function count_user_posts_for_multiple_types( $userid, $post_types = [ 'post' ], $public_only = false ) {
+	$numposts = 0;
+
+    foreach( $post_types as $post_type ) {
+        $numposts += count_user_posts_for_single_type( $userid, $post_type, $public_only );
+    }
+
+	return $numposts;
+}
+
 /**
  * Gets the number of posts a user has written.
  *
@@ -552,11 +631,13 @@ function wp_validate_logged_in_cookie( $user_id ) {
  * @return string Number of posts the user has written in this post type.
  */
 function count_user_posts( $userid, $post_type = 'post', $public_only = false ) {
-	global $wpdb;
-
-	$where = get_posts_by_author_sql( $post_type, true, $userid, $public_only );
+	$count = 0;
 
-	$count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts $where" );
+	if ( is_array( $post_type ) ) {
+        $count = count_user_posts_for_multiple_types( $userid, $post_type, $public_only );
+    } else {
+		$count = count_user_posts_for_single_type( $userid, $post_type, $public_only );
+	}
 
 	/**
 	 * Filters the number of posts a user has written.
@@ -586,19 +667,13 @@ function count_user_posts( $userid, $post_type = 'post', $public_only = false )
  * @return string[] Amount of posts each user has written, as strings, keyed by user ID.
  */
 function count_many_users_posts( $users, $post_type = 'post', $public_only = false ) {
-	global $wpdb;
-
 	$count = array();
 	if ( empty( $users ) || ! is_array( $users ) ) {
 		return $count;
 	}
 
-	$userlist = implode( ',', array_map( 'absint', $users ) );
-	$where    = get_posts_by_author_sql( $post_type, true, null, $public_only );
-
-	$result = $wpdb->get_results( "SELECT post_author, COUNT(*) FROM $wpdb->posts $where AND post_author IN ($userlist) GROUP BY post_author", ARRAY_N );
-	foreach ( $result as $row ) {
-		$count[ $row[0] ] = $row[1];
+	foreach ( $users as $user_id ) {
+		$count[ $user_id ] = count_user_posts( $user_id, $post_type, $public_only );
 	}
 
 	foreach ( $users as $id ) {
