diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp
index 0225fda9ea9d..5aa79f070c54 100644
--- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp
+++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp
@@ -102,6 +102,10 @@ void PerformanceTracer::collectEvents(
 void PerformanceTracer::reportMark(
     const std::string_view& name,
     uint64_t start) {
+  if (!tracing_) {
+    return;
+  }
+
   std::lock_guard<std::mutex> lock(mutex_);
   if (!tracing_) {
     return;
@@ -122,6 +126,10 @@ void PerformanceTracer::reportMeasure(
     uint64_t start,
     uint64_t duration,
     const std::optional<DevToolsTrackEntryPayload>& trackMetadata) {
+  if (!tracing_) {
+    return;
+  }
+
   std::lock_guard<std::mutex> lock(mutex_);
   if (!tracing_) {
     return;
@@ -160,6 +168,10 @@ void PerformanceTracer::reportMeasure(
 }
 
 void PerformanceTracer::reportProcess(uint64_t id, const std::string& name) {
+  if (!tracing_) {
+    return;
+  }
+
   std::lock_guard<std::mutex> lock(mutex_);
   if (!tracing_) {
     return;
@@ -177,6 +189,10 @@ void PerformanceTracer::reportProcess(uint64_t id, const std::string& name) {
 }
 
 void PerformanceTracer::reportThread(uint64_t id, const std::string& name) {
+  if (!tracing_) {
+    return;
+  }
+
   std::lock_guard<std::mutex> lock(mutex_);
   if (!tracing_) {
     return;
diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h
index cf80b68080c0..afb693d2a479 100644
--- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h
+++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h
@@ -40,6 +40,17 @@ class PerformanceTracer {
    */
   bool stopTracing();
 
+  /**
+   * Returns whether the tracer is currently tracing. This can be useful to
+   * avoid doing expensive work (like formatting strings) if tracing is not
+   * enabled.
+   */
+  bool isTracing() const {
+    // This is not thread safe but it's only a performance optimization. The
+    // call to report marks and measures is already thread safe.
+    return tracing_;
+  }
+
   /**
    * Flush out buffered CDP Trace Events using the given callback.
    */
diff --git a/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.cpp b/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.cpp
index cff8e8678a77..14b0c3f7ef94 100644
--- a/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.cpp
+++ b/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformance.cpp
@@ -14,10 +14,8 @@
 #include <cxxreact/JSExecutor.h>
 #include <cxxreact/ReactMarker.h>
 #include <jsi/instrumentation.h>
-#include <jsinspector-modern/tracing/CdpTracing.h>
 #include <react/performance/timeline/PerformanceEntryReporter.h>
 #include <react/performance/timeline/PerformanceObserver.h>
-#include <reactperflogger/ReactPerfettoLogger.h>
 
 #include "NativePerformance.h"
 
@@ -25,10 +23,6 @@
 #include "Plugins.h"
 #endif
 
-#ifdef WITH_PERFETTO
-#include <reactperflogger/ReactPerfetto.h>
-#endif
-
 std::shared_ptr<facebook::react::TurboModule> NativePerformanceModuleProvider(
     std::shared_ptr<facebook::react::CallInvoker> jsInvoker) {
   return std::make_shared<facebook::react::NativePerformance>(
@@ -39,33 +33,6 @@ namespace facebook::react {
 
 namespace {
 
-#if defined(__clang__)
-#define NO_DESTROY [[clang::no_destroy]]
-#else
-#define NO_DESTROY
-#endif
-
-NO_DESTROY const std::string TRACK_PREFIX = "Track:";
-
-std::tuple<std::optional<std::string>, std::string_view> parseTrackName(
-    const std::string& name) {
-  // Until there's a standard way to pass through track information, parse it
-  // manually, e.g., "Track:Foo:Event name"
-  // https://github.com/w3c/user-timing/issues/109
-  std::optional<std::string> trackName;
-  std::string_view eventName(name);
-  if (name.starts_with(TRACK_PREFIX)) {
-    const auto trackNameDelimiter = name.find(':', TRACK_PREFIX.length());
-    if (trackNameDelimiter != std::string::npos) {
-      trackName = name.substr(
-          TRACK_PREFIX.length(), trackNameDelimiter - TRACK_PREFIX.length());
-      eventName = std::string_view(name).substr(trackNameDelimiter + 1);
-    }
-  }
-
-  return std::make_tuple(trackName, eventName);
-}
-
 class PerformanceObserverWrapper : public jsi::NativeState {
  public:
   explicit PerformanceObserverWrapper(
@@ -103,11 +70,7 @@ std::shared_ptr<PerformanceObserver> tryGetObserver(
 } // namespace
 
 NativePerformance::NativePerformance(std::shared_ptr<CallInvoker> jsInvoker)
-    : NativePerformanceCxxSpec(std::move(jsInvoker)) {
-#ifdef WITH_PERFETTO
-  initializePerfetto();
-#endif
-}
+    : NativePerformanceCxxSpec(std::move(jsInvoker)) {}
 
 double NativePerformance::now(jsi::Runtime& /*rt*/) {
   return JSExecutor::performanceNow();
@@ -117,12 +80,8 @@ double NativePerformance::markWithResult(
     jsi::Runtime& rt,
     std::string name,
     std::optional<double> startTime) {
-  auto [trackName, eventName] = parseTrackName(name);
   auto entry =
       PerformanceEntryReporter::getInstance()->reportMark(name, startTime);
-
-  ReactPerfettoLogger::mark(eventName, entry.startTime, trackName);
-
   return entry.startTime;
 }
 
@@ -134,26 +93,8 @@ std::tuple<double, double> NativePerformance::measureWithResult(
     std::optional<double> duration,
     std::optional<std::string> startMark,
     std::optional<std::string> endMark) {
-  auto [trackName, eventName] = parseTrackName(name);
-
-  std::optional<jsinspector_modern::DevToolsTrackEntryPayload> trackMetadata;
-
-  if (trackName.has_value()) {
-    trackMetadata = {.track = trackName.value()};
-  }
-
   auto entry = PerformanceEntryReporter::getInstance()->reportMeasure(
-      eventName,
-      startTime,
-      endTime,
-      duration,
-      startMark,
-      endMark,
-      trackMetadata);
-
-  ReactPerfettoLogger::measure(
-      eventName, entry.startTime, entry.startTime + entry.duration, trackName);
-
+      name, startTime, endTime, duration, startMark, endMark);
   return std::tuple{entry.startTime, entry.duration};
 }
 
diff --git a/packages/react-native/ReactCommon/react/performance/timeline/CMakeLists.txt b/packages/react-native/ReactCommon/react/performance/timeline/CMakeLists.txt
index 335539e390a0..e44b66d14e76 100644
--- a/packages/react-native/ReactCommon/react/performance/timeline/CMakeLists.txt
+++ b/packages/react-native/ReactCommon/react/performance/timeline/CMakeLists.txt
@@ -20,5 +20,6 @@ add_library(react_performance_timeline OBJECT ${react_performance_timeline_SRC})
 target_include_directories(react_performance_timeline PUBLIC ${REACT_COMMON_DIR})
 target_link_libraries(react_performance_timeline
         jsinspector_tracing
+        reactperflogger
         react_timing
         react_cxxreact)
diff --git a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp
index 36c947cda77a..f0e9d47512ea 100644
--- a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp
+++ b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp
@@ -10,6 +10,11 @@
 #include <cxxreact/JSExecutor.h>
 #include <jsinspector-modern/tracing/PerformanceTracer.h>
 #include <react/featureflags/ReactNativeFeatureFlags.h>
+#include <reactperflogger/ReactPerfettoLogger.h>
+
+#ifdef WITH_PERFETTO
+#include <reactperflogger/ReactPerfetto.h>
+#endif
 
 namespace facebook::react {
 
@@ -33,6 +38,33 @@ uint64_t timestampToMicroseconds(DOMHighResTimeStamp timestamp) {
   return static_cast<uint64_t>(timestamp * 1000);
 }
 
+#if defined(__clang__)
+#define NO_DESTROY [[clang::no_destroy]]
+#else
+#define NO_DESTROY
+#endif
+
+NO_DESTROY const std::string TRACK_PREFIX = "Track:";
+
+std::tuple<std::optional<std::string>, std::string_view> parseTrackName(
+    const std::string& name) {
+  // Until there's a standard way to pass through track information, parse it
+  // manually, e.g., "Track:Foo:Event name"
+  // https://github.com/w3c/user-timing/issues/109
+  std::optional<std::string> trackName;
+  std::string_view eventName(name);
+  if (name.starts_with(TRACK_PREFIX)) {
+    const auto trackNameDelimiter = name.find(':', TRACK_PREFIX.length());
+    if (trackNameDelimiter != std::string::npos) {
+      trackName = name.substr(
+          TRACK_PREFIX.length(), trackNameDelimiter - TRACK_PREFIX.length());
+      eventName = std::string_view(name).substr(trackNameDelimiter + 1);
+    }
+  }
+
+  return std::make_tuple(trackName, eventName);
+}
+
 } // namespace
 
 std::shared_ptr<PerformanceEntryReporter>&
@@ -42,7 +74,11 @@ PerformanceEntryReporter::getInstance() {
 }
 
 PerformanceEntryReporter::PerformanceEntryReporter()
-    : observerRegistry_(std::make_unique<PerformanceObserverRegistry>()) {}
+    : observerRegistry_(std::make_unique<PerformanceObserverRegistry>()) {
+#ifdef WITH_PERFETTO
+  initializePerfetto();
+#endif
+}
 
 DOMHighResTimeStamp PerformanceEntryReporter::getCurrentTimeStamp() const {
   return timeStampProvider_ != nullptr ? timeStampProvider_()
@@ -135,26 +171,28 @@ void PerformanceEntryReporter::clearEntries(
 PerformanceEntry PerformanceEntryReporter::reportMark(
     const std::string& name,
     const std::optional<DOMHighResTimeStamp>& startTime) {
+  // Resolve timings
   auto startTimeVal = startTime ? *startTime : getCurrentTimeStamp();
   const auto entry = PerformanceEntry{
       .name = name,
       .entryType = PerformanceEntryType::MARK,
       .startTime = startTimeVal};
 
+  traceMark(entry);
+
+  // Add to buffers & notify observers
   {
     std::unique_lock lock(buffersMutex_);
     markBuffer_.add(entry);
   }
 
-  jsinspector_modern::PerformanceTracer::getInstance().reportMark(
-      name, timestampToMicroseconds(startTimeVal));
-
   observerRegistry_->queuePerformanceEntry(entry);
+
   return entry;
 }
 
 PerformanceEntry PerformanceEntryReporter::reportMeasure(
-    const std::string_view& name,
+    const std::string& name,
     DOMHighResTimeStamp startTime,
     DOMHighResTimeStamp endTime,
     const std::optional<DOMHighResTimeStamp>& duration,
@@ -181,18 +219,16 @@ PerformanceEntry PerformanceEntryReporter::reportMeasure(
       .startTime = startTimeVal,
       .duration = durationVal};
 
+  traceMeasure(entry);
+
+  // Add to buffers & notify observers
   {
     std::unique_lock lock(buffersMutex_);
     measureBuffer_.add(entry);
   }
 
-  jsinspector_modern::PerformanceTracer::getInstance().reportMeasure(
-      name,
-      timestampToMicroseconds(startTimeVal),
-      timestampToMicroseconds(durationVal),
-      trackMetadata);
-
   observerRegistry_->queuePerformanceEntry(entry);
+
   return entry;
 }
 
@@ -257,4 +293,52 @@ void PerformanceEntryReporter::reportLongTask(
   observerRegistry_->queuePerformanceEntry(entry);
 }
 
+void PerformanceEntryReporter::traceMark(const PerformanceEntry& entry) const {
+  auto& performanceTracer =
+      jsinspector_modern::PerformanceTracer::getInstance();
+  if (ReactPerfettoLogger::isTracing() || performanceTracer.isTracing()) {
+    auto [trackName, eventName] = parseTrackName(entry.name);
+
+    if (performanceTracer.isTracing()) {
+      performanceTracer.reportMark(
+          entry.name, timestampToMicroseconds(entry.startTime));
+    }
+
+    if (ReactPerfettoLogger::isTracing()) {
+      ReactPerfettoLogger::mark(eventName, entry.startTime, trackName);
+    }
+  }
+}
+
+void PerformanceEntryReporter::traceMeasure(
+    const PerformanceEntry& entry) const {
+  auto& performanceTracer =
+      jsinspector_modern::PerformanceTracer::getInstance();
+  if (performanceTracer.isTracing() || ReactPerfettoLogger::isTracing()) {
+    auto [trackName, eventName] = parseTrackName(entry.name);
+
+    if (performanceTracer.isTracing()) {
+      std::optional<jsinspector_modern::DevToolsTrackEntryPayload>
+          trackMetadata;
+
+      if (trackName.has_value()) {
+        trackMetadata = {.track = trackName.value()};
+      }
+      performanceTracer.reportMeasure(
+          eventName,
+          timestampToMicroseconds(entry.startTime),
+          timestampToMicroseconds(entry.duration),
+          trackMetadata);
+    }
+
+    if (ReactPerfettoLogger::isTracing()) {
+      ReactPerfettoLogger::measure(
+          eventName,
+          entry.startTime,
+          entry.startTime + entry.duration,
+          trackName);
+    }
+  }
+}
+
 } // namespace facebook::react
diff --git a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h
index 96730861a9ea..8b907cd64938 100644
--- a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h
+++ b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h
@@ -82,7 +82,7 @@ class PerformanceEntryReporter {
       const std::optional<DOMHighResTimeStamp>& startTime = std::nullopt);
 
   PerformanceEntry reportMeasure(
-      const std::string_view& name,
+      const std::string& name,
       double startTime,
       double endTime,
       const std::optional<double>& duration = std::nullopt,
@@ -148,6 +148,9 @@ class PerformanceEntryReporter {
     }
     throw std::logic_error("Unhandled PerformanceEntryType");
   }
+
+  void traceMark(const PerformanceEntry& entry) const;
+  void traceMeasure(const PerformanceEntry& entry) const;
 };
 
 } // namespace facebook::react
diff --git a/packages/react-native/ReactCommon/react/performance/timeline/React-performancetimeline.podspec b/packages/react-native/ReactCommon/react/performance/timeline/React-performancetimeline.podspec
index fd0bcd29a0a0..553c9e81cd77 100644
--- a/packages/react-native/ReactCommon/react/performance/timeline/React-performancetimeline.podspec
+++ b/packages/react-native/ReactCommon/react/performance/timeline/React-performancetimeline.podspec
@@ -55,5 +55,6 @@ Pod::Spec.new do |s|
   add_dependency(s, "React-jsinspectortracing", :framework_name => 'jsinspector_moderntracing')
   s.dependency "React-timing"
   s.dependency "React-cxxreact"
+  s.dependency "React-perflogger"
   s.dependency "RCT-Folly", folly_version
 end
diff --git a/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfettoLogger.cpp b/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfettoLogger.cpp
index 3e8e50b320ba..94ccfcc23f44 100644
--- a/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfettoLogger.cpp
+++ b/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfettoLogger.cpp
@@ -29,6 +29,14 @@ std::string toPerfettoTrackName(
 } // namespace
 #endif
 
+/* static */ bool ReactPerfettoLogger::isTracing() {
+#ifdef WITH_PERFETTO
+  return TRACE_EVENT_CATEGORY_ENABLED("react-native");
+#else
+  return false;
+#endif
+}
+
 /* static */ void ReactPerfettoLogger::measure(
     const std::string_view& eventName,
     double startTime,
diff --git a/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfettoLogger.h b/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfettoLogger.h
index c08c69503545..a85d06a5b390 100644
--- a/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfettoLogger.h
+++ b/packages/react-native/ReactCommon/reactperflogger/reactperflogger/ReactPerfettoLogger.h
@@ -20,6 +20,8 @@ namespace facebook::react {
  */
 class ReactPerfettoLogger {
  public:
+  static bool isTracing();
+
   static void mark(
       const std::string_view& eventName,
       double startTime,
diff --git a/packages/react-native/src/private/webapis/performance/__tests__/Performance-benchmark-itest.js b/packages/react-native/src/private/webapis/performance/__tests__/Performance-benchmark-itest.js
new file mode 100644
index 000000000000..2e709e8d8ca6
--- /dev/null
+++ b/packages/react-native/src/private/webapis/performance/__tests__/Performance-benchmark-itest.js
@@ -0,0 +1,123 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow strict-local
+ * @format
+ * @oncall react_native
+ * @fantom_mode opt
+ */
+
+import '../../../../../Libraries/Core/InitializeCore.js';
+
+import Fantom from '@react-native/fantom';
+
+const clearMarksAndMeasures = () => {
+  performance.clearMarks();
+  performance.clearMeasures();
+};
+
+Fantom.unstable_benchmark
+  .suite('Performance API')
+  .test(
+    'mark (default)',
+    () => {
+      performance.mark('mark');
+    },
+    {
+      afterEach: clearMarksAndMeasures,
+    },
+  )
+  .test(
+    'mark (with custom startTime)',
+    () => {
+      performance.mark('mark', {
+        startTime: 100,
+      });
+    },
+    {
+      afterEach: clearMarksAndMeasures,
+    },
+  )
+  .test(
+    'measure (with start and end timestamps)',
+    () => {
+      performance.measure('measure', {
+        start: 100,
+        end: 300,
+      });
+    },
+    {
+      afterEach: clearMarksAndMeasures,
+    },
+  )
+  .test(
+    'measure (with mark names)',
+    () => {
+      performance.measure('measure', 'measure-start', 'measure-end');
+    },
+    {
+      beforeEach: () => {
+        performance.mark('measure-start', {
+          startTime: 100,
+        });
+        performance.mark('measure-end', {
+          startTime: 300,
+        });
+      },
+      afterEach: clearMarksAndMeasures,
+    },
+  )
+  .test(
+    'clearMarks',
+    () => {
+      performance.clearMarks('mark');
+    },
+    {
+      beforeEach: () => performance.mark('mark'),
+    },
+  )
+  .test(
+    'clearMeasures',
+    () => {
+      performance.clearMeasures('measure');
+    },
+    {
+      beforeEach: () =>
+        performance.measure('measure', {
+          start: 100,
+          end: 300,
+        }),
+    },
+  )
+  .test('mark + clearMarks', () => {
+    performance.mark('mark');
+    performance.clearMarks('mark');
+  })
+  .test('measure + clearMeasures (with start and end timestamps)', () => {
+    performance.measure('measure', {
+      start: 100,
+      end: 300,
+    });
+    performance.clearMeasures('measure');
+  })
+  .test(
+    'measure + clearMeasures (with mark names)',
+    () => {
+      performance.measure('measure', 'measure-start', 'measure-end');
+      performance.clearMeasures('measure');
+    },
+    {
+      beforeEach: () => {
+        performance.mark('measure-start', {
+          startTime: 100,
+        });
+        performance.mark('measure-end', {
+          startTime: 300,
+        });
+      },
+      afterEach: clearMarksAndMeasures,
+    },
+  );
