diff --git a/CHANGELOG b/CHANGELOG
index c64017f75b9330580addf520f8a8fdbb36225013..e956d074d728073e1ce8a28c3cf0b414f00abde3 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 Please view this file on the master branch, on stable branches it's out of date.
 
 v 8.2.0 (unreleased)
+  - Improved performance of replacing references in comments
   - Fix duplicate repositories in GitHub import page (Stan Hu)
   - Redirect to a default path if HTTP_REFERER is not set (Stan Hu)
   - Show last project commit to default branch on project home page
diff --git a/lib/gitlab/markdown/reference_filter.rb b/lib/gitlab/markdown/reference_filter.rb
index adaca78ba27282434cedfe5de7d227ebc4a1e18e..a4c560f578cae024b180fb3d05c0f0dd6e85f215 100644
--- a/lib/gitlab/markdown/reference_filter.rb
+++ b/lib/gitlab/markdown/reference_filter.rb
@@ -15,7 +15,7 @@ module Gitlab
       LazyReference = Struct.new(:klass, :ids) do
         def self.load(refs)
           lazy_references, values = refs.partition { |ref| ref.is_a?(self) }
-          
+
           lazy_values = lazy_references.group_by(&:klass).flat_map do |klass, refs|
             ids = refs.flat_map(&:ids)
             klass.where(id: ids)
@@ -107,10 +107,10 @@ module Gitlab
         return doc if project.nil?
 
         search_text_nodes(doc).each do |node|
-          content = node.to_html
-
-          next unless content.match(pattern)
           next if ignored_ancestry?(node)
+          next unless node.text =~ pattern
+
+          content = node.to_html
 
           html = yield content
 
diff --git a/spec/benchmarks/lib/gitlab/markdown/reference_filter_spec.rb b/spec/benchmarks/lib/gitlab/markdown/reference_filter_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..34cd9f7e4eb398bd64d8d65226d15bc7488767c4
--- /dev/null
+++ b/spec/benchmarks/lib/gitlab/markdown/reference_filter_spec.rb
@@ -0,0 +1,41 @@
+require 'spec_helper'
+
+describe Gitlab::Markdown::ReferenceFilter, benchmark: true do
+  let(:input) do
+    html = <<-EOF
+<p>Hello @alice and @bob, how are you doing today?</p>
+<p>This is simple @dummy text to see how the @ReferenceFilter class performs
+when @processing HTML.</p>
+    EOF
+
+    Nokogiri::HTML.fragment(html)
+  end
+
+  let(:project) { create(:empty_project) }
+
+  let(:filter) { described_class.new(input, project: project) }
+
+  describe '#replace_text_nodes_matching' do
+    let(:iterations) { 6000 }
+
+    describe 'with identical input and output HTML' do
+      benchmark_subject do
+        filter.replace_text_nodes_matching(User.reference_pattern) do |content|
+          content
+        end
+      end
+
+      it { is_expected.to iterate_per_second(iterations) }
+    end
+
+    describe 'with different input and output HTML' do
+      benchmark_subject do
+        filter.replace_text_nodes_matching(User.reference_pattern) do |content|
+          '@eve'
+        end
+      end
+
+      it { is_expected.to iterate_per_second(iterations) }
+    end
+  end
+end