Explorar el Código

feat: add profiler(dev) rightside-tab (#11668)

* perf: add frontend.handler.profiler
* perf: add frontend.components.profiler
rcmerci hace 1 año
padre
commit
28cfdf682a

+ 60 - 0
src/main/frontend/components/profiler.cljs

@@ -0,0 +1,60 @@
+(ns frontend.components.profiler
+  "Profiler UI"
+  (:require [fipp.edn :as fipp]
+            [frontend.handler.profiler :as profiler-handler]
+            [frontend.util :as util]
+            [logseq.shui.ui :as shui]
+            [rum.core :as rum]))
+
+(rum/defcs profiler < rum/reactive
+  (rum/local nil ::reports)
+  (rum/local nil ::register-fn-name)
+  [state]
+  (let [profiling-fns (keys (rum/react profiler-handler/*fn-symbol->origin-fn))
+        *reports (get state ::reports)
+        *register-fn-name (get state ::register-fn-name)]
+    [:div
+     [:b "Profiling fns:"]
+     [:div.pb-4
+      (for [f-name profiling-fns]
+        [:div.flex.flex-row.items-center.gap-2
+         [:pre.select-text (str f-name)]
+         [:a.inline.close.flex.transition-opacity.duration-300.ease-in
+          {:title "Unregister"
+           :on-pointer-down
+           (fn [e]
+             (util/stop e)
+             (profiler-handler/unregister-fn! f-name))}
+          (shui/tabler-icon "x")]])]
+     [:div.flex.flex-row.items-center.gap-2
+      (shui/button
+       {:on-click (fn []
+                    (when-let [fn-sym (some-> @*register-fn-name symbol)]
+                      (profiler-handler/register-fn! fn-sym)))}
+       "Register fn")
+      [:input.form-input.my-2.py-1
+       {:on-change (fn [e] (reset! *register-fn-name (util/evalue e)))
+        :on-focus (fn [e] (let [v (.-value (.-target e))]
+                            (when (= v "input fn name here")
+                              (set! (.-value (.-target e)) ""))))
+        :placeholder "input fn name here"}]]
+     [:hr]
+     [:div.flex.gap-2.flex-wrap.items-center.pb-3
+      (shui/button
+       {:size :sm
+        :on-click (fn [_] (reset! *reports (profiler-handler/profile-report)))}
+       (shui/tabler-icon "refresh") "Refresh reports")
+      (shui/button
+       {:size :sm
+        :on-click (fn [_] (profiler-handler/reset-report!)
+                    (reset! *reports (profiler-handler/profile-report)))}
+       (shui/tabler-icon "x") "Reset reports")]
+     (let [update-time-sum
+           (fn [m] (update-vals m (fn [m2] (update-vals m2 #(.toFixed % 6)))))]
+       [:div.pb-4
+        [:pre.select-text
+         (when @*reports
+           (-> @*reports
+               (update :time-sum update-time-sum)
+               (fipp/pprint {:width 20})
+               with-out-str))]])]))

+ 12 - 2
src/main/frontend/components/right_sidebar.cljs

@@ -22,7 +22,8 @@
             [frontend.db.rtc.debug-ui :as rtc-debug-ui]
             [frontend.handler.route :as route-handler]
             [logseq.db :as ldb]
-            [frontend.components.icon :as icon]))
+            [frontend.components.icon :as icon]
+            [frontend.components.profiler :as profiler]))
 
 (rum/defc toggle
   []
@@ -133,6 +134,10 @@
     [[:.flex.items-center (ui/icon "cloud" {:class "text-md mr-2"}) "(Dev) RTC"]
      (rtc-debug-ui/rtc-debug-ui)]
 
+    :profiler
+    [[:.flex.items-center (ui/icon "cloud" {:class "text-md mr-2"}) "(Dev) Profiler"]
+     (profiler/profiler)]
+
     ["" [:span]]))
 
 (defonce *drag-to
@@ -407,7 +412,12 @@
           [:div.text-sm
            [:button.button.cp__right-sidebar-settings-btn {:on-click (fn [_e]
                                                                        (state/sidebar-add-block! repo "rtc" :rtc))}
-            "(Dev) RTC"]])]]
+            "(Dev) RTC"]])
+        (when (state/sub [:ui/developer-mode?])
+          [:div.text-sm
+           [:button.button.cp__right-sidebar-settings-btn {:on-click (fn [_e]
+                                                                       (state/sidebar-add-block! repo "profiler" :profiler))}
+            "(Dev) Profiler"]])]]
 
       [:.sidebar-item-list.flex-1.scrollbar-spacing.px-2
        (if @*anim-finished?

+ 62 - 0
src/main/frontend/handler/profiler.cljs

@@ -0,0 +1,62 @@
+(ns frontend.handler.profiler
+  "Provides fns for profiling.
+  TODO: support both main thread and worker thread."
+  (:require [goog.object :as g]))
+
+(def ^:private *fn-symbol->key->call-count (volatile! {}))
+(def ^:private *fn-symbol->key->time-sum (volatile! {}))
+
+(def *fn-symbol->origin-fn (atom {}))
+
+(defn- get-profile-fn
+  [fn-sym original-fn custom-key-fn]
+  (fn profile-fn-inner [& args]
+    (let [start (system-time)
+          r (apply original-fn args)
+          elapsed-time (- (system-time) start)
+          k (when custom-key-fn (custom-key-fn r))]
+      (vswap! *fn-symbol->key->call-count update-in [fn-sym :total] inc)
+      (vswap! *fn-symbol->key->time-sum update-in [fn-sym :total] #(+ % elapsed-time))
+      (when k
+        (vswap! *fn-symbol->key->call-count update-in [fn-sym k] inc)
+        (vswap! *fn-symbol->key->time-sum update-in [fn-sym k] #(+ % elapsed-time)))
+      r)))
+
+(defn register-fn!
+  [fn-sym & {:keys [custom-key-fn] :as _opts}]
+  (assert (qualified-symbol? fn-sym))
+  (let [ns (namespace fn-sym)
+        s (munge (name fn-sym))]
+    (if-let [original-fn (find-ns-obj (str ns "." s))]
+      (let [profiled-fn (get-profile-fn fn-sym original-fn custom-key-fn)]
+        (swap! *fn-symbol->origin-fn assoc fn-sym original-fn)
+        (g/set (find-ns-obj ns) s profiled-fn))
+      (throw (ex-info (str "fn-sym not found: " fn-sym) {})))))
+
+(defn unregister-fn!
+  [fn-sym]
+  (let [ns (namespace fn-sym)
+        s (munge (name fn-sym))]
+    (vswap! *fn-symbol->key->call-count dissoc fn-sym)
+    (vswap! *fn-symbol->key->time-sum dissoc fn-sym)
+    (when-let [origin-fn (get @*fn-symbol->origin-fn fn-sym)]
+      (some-> (find-ns-obj ns) (g/set s origin-fn))
+      (swap! *fn-symbol->origin-fn dissoc fn-sym))))
+
+(defn reset-report!
+  []
+  (vreset! *fn-symbol->key->call-count {})
+  (vreset! *fn-symbol->key->time-sum {}))
+
+(defn profile-report
+  []
+  {:call-count @*fn-symbol->key->call-count
+   :time-sum @*fn-symbol->key->time-sum})
+
+(comment
+  (register-fn! 'datascript.core/entity)
+  (prn :profiling (keys @*fn-symbol->origin-fn))
+  (prn :report)
+  (pprint/pprint (profile-report))
+  (reset-report!)
+  (unregister-fn! 'datascript.core/entity))