mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[fus, code vision] aggregate code vision events - create hists and send them on closing of editor IDEA-331947
GitOrigin-RevId: f0d85f50dbb6c45f828e7912faf7167c9906a9f4
This commit is contained in:
committed by
intellij-monorepo-bot
parent
0c27b276dc
commit
4ea6027256
+69
-13
@@ -3,22 +3,78 @@ package com.intellij.codeInsight.hints.codeVision
|
||||
|
||||
import com.intellij.internal.statistic.eventLog.EventLogGroup
|
||||
import com.intellij.internal.statistic.eventLog.events.EventFields
|
||||
import com.intellij.internal.statistic.eventLog.events.EventId3
|
||||
import com.intellij.internal.statistic.eventLog.events.IntListEventField
|
||||
import com.intellij.internal.statistic.eventLog.events.VarargEventId
|
||||
import com.intellij.internal.statistic.service.fus.collectors.CounterUsagesCollector
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import com.intellij.lang.Language
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Internal
|
||||
object CodeVisionFusCollector : CounterUsagesCollector() {
|
||||
private val GROUP = EventLogGroup("daemon.code.vision", 2)
|
||||
|
||||
// TODO make some kind of throttling
|
||||
val CODE_VISION_FINISHED = GROUP.registerEvent(
|
||||
"finished",
|
||||
EventFields.DurationMs,
|
||||
EventFields.Class("provider_class"),
|
||||
EventFields.Language
|
||||
)
|
||||
|
||||
val ANNOTATION_LOADED = GROUP.registerEvent("vcs.annotation.loaded", EventFields.DurationMs)
|
||||
private val GROUP = EventLogGroup("daemon.code.vision", 3)
|
||||
|
||||
override fun getGroup(): EventLogGroup = GROUP
|
||||
|
||||
val HISTOGRAM_FIELD: IntListEventField = EventFields.IntList("histogram")
|
||||
|
||||
val PROVIDER_CLASS_FIELD = EventFields.Class("provider_class")
|
||||
|
||||
|
||||
val HISTOGRAM_BUCKETS = longArrayOf(0, 100, 500, 1000, 5000, 10000, 30000, 100000)
|
||||
|
||||
/**
|
||||
* histogram buckets are mentioned in [HISTOGRAM_BUCKETS]
|
||||
*/
|
||||
val CODE_VISION_DURATION_HISTOGRAM: VarargEventId = GROUP.registerVarargEvent("code.vision.duration",
|
||||
HISTOGRAM_FIELD,
|
||||
EventFields.Language,
|
||||
EventFields.Size,
|
||||
PROVIDER_CLASS_FIELD)
|
||||
|
||||
val VCS_ANNOTATION_CALCULATION_DURATION_HISTOGRAM: EventId3<List<Int>, Language?, Int> = GROUP.registerEvent("vcs.annotation.calculation",
|
||||
EventFields.IntList(
|
||||
"histogram"),
|
||||
EventFields.Language,
|
||||
EventFields.Size)
|
||||
|
||||
internal val PROVIDER_STORAGE_KEY: Key<CodeVisionFusProviderStorage> = Key.create<CodeVisionFusProviderStorage>("code.vision.fus.provider.storage")
|
||||
internal val VCS_ANNOTATION_HISTOGRAM_KEY: Key<FusHistogramBuilder> = Key.create<FusHistogramBuilder>("vcs.annotation.histogram")
|
||||
|
||||
internal fun reportCodeVisionProviderDuration(editor: Editor, language: Language, durationMs: Long, providerClass: Class<*>) {
|
||||
val fusProviderStorage = getProviderStorage(editor, language)
|
||||
fusProviderStorage.logProviderCollectionDuration(providerClass, durationMs)
|
||||
}
|
||||
|
||||
private fun getProviderStorage(editor: Editor, language: Language) : CodeVisionFusProviderStorage {
|
||||
val providerStorage: CodeVisionFusProviderStorage? = editor.getUserData(PROVIDER_STORAGE_KEY)
|
||||
// not race free - but it is not a problem. We are OK if we lose data in this case
|
||||
if (providerStorage == null) {
|
||||
val newStorage = CodeVisionFusProviderStorage(language)
|
||||
editor.putUserData(PROVIDER_STORAGE_KEY, newStorage)
|
||||
return newStorage
|
||||
}
|
||||
return providerStorage
|
||||
}
|
||||
|
||||
@Internal
|
||||
fun reportVcsAnnotationDuration(psiFile: PsiFile, durationMs: Long) {
|
||||
val histogramBuilder = getHistogramBuilder(psiFile)
|
||||
histogramBuilder.addValue(durationMs)
|
||||
}
|
||||
|
||||
private fun getHistogramBuilder(psiFile: PsiFile) : FusHistogramBuilder {
|
||||
val vcsAnnotationHistogramBuilder: FusHistogramBuilder? = VCS_ANNOTATION_HISTOGRAM_KEY.get(psiFile)
|
||||
|
||||
// not race free - but it is not a problem. We are OK if we lose data in this case
|
||||
if (vcsAnnotationHistogramBuilder == null) {
|
||||
val newHistogram = FusHistogramBuilder(HISTOGRAM_BUCKETS)
|
||||
psiFile.putUserData(VCS_ANNOTATION_HISTOGRAM_KEY, newHistogram)
|
||||
return newHistogram
|
||||
}
|
||||
return vcsAnnotationHistogramBuilder
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.hints.codeVision
|
||||
|
||||
import com.intellij.codeInsight.hints.codeVision.CodeVisionFusCollector.HISTOGRAM_FIELD
|
||||
import com.intellij.internal.statistic.eventLog.events.EventFields
|
||||
import com.intellij.internal.statistic.eventLog.events.EventPair
|
||||
import com.intellij.internal.statistic.utils.StatisticsUtil
|
||||
import com.intellij.lang.Language
|
||||
import com.intellij.openapi.editor.event.EditorFactoryEvent
|
||||
import com.intellij.openapi.editor.event.EditorFactoryListener
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.util.concurrency.AppExecutorUtil
|
||||
import kotlinx.coroutines.Runnable
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class CodeVisionFusEditorListener : EditorFactoryListener {
|
||||
override fun editorReleased(event: EditorFactoryEvent) {
|
||||
val editor = event.editor
|
||||
val project = editor.project ?: return
|
||||
val histogramBuilder = editor.getUserData(CodeVisionFusCollector.PROVIDER_STORAGE_KEY)
|
||||
if (histogramBuilder != null) {
|
||||
val fusData: CodeVisionFusData = histogramBuilder.flush()
|
||||
val document = editor.document
|
||||
val psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document)
|
||||
|
||||
val vcsAnnotationHistogram: FusHistogram? = psiFile?.getUserData(CodeVisionFusCollector.VCS_ANNOTATION_HISTOGRAM_KEY)?.build()
|
||||
// we remove it because editor may be closed, but the file remains. In order not to report its data several times, we need to remove
|
||||
psiFile?.putUserData(CodeVisionFusCollector.VCS_ANNOTATION_HISTOGRAM_KEY, null)
|
||||
val textLength = StatisticsUtil.roundToPowerOfTwo(document.textLength)
|
||||
|
||||
AppExecutorUtil.getAppExecutorService().submit(Runnable {
|
||||
if (vcsAnnotationHistogram != null) {
|
||||
CodeVisionFusCollector.VCS_ANNOTATION_CALCULATION_DURATION_HISTOGRAM.log(project, vcsAnnotationHistogram.buckets.toList(), psiFile.language, textLength)
|
||||
}
|
||||
|
||||
val language = fusData.language
|
||||
for ((providerClass, histogram) in fusData.providerClassToHistogram) {
|
||||
CodeVisionFusCollector.CODE_VISION_DURATION_HISTOGRAM.log(project, listOf(
|
||||
EventPair(HISTOGRAM_FIELD, histogram.toList()),
|
||||
EventPair(EventFields.Language, language),
|
||||
EventPair(EventFields.Size, textLength),
|
||||
EventPair(CodeVisionFusCollector.PROVIDER_CLASS_FIELD, providerClass)
|
||||
))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class CodeVisionFusProviderStorage(val language: Language) {
|
||||
private val providerClassToHistogram: ConcurrentHashMap<Class<*>, FusHistogramBuilder> = ConcurrentHashMap()
|
||||
|
||||
fun logProviderCollectionDuration(providerClass: Class<*>, durationMs: Long) {
|
||||
val fusHistogramBuilder = providerClassToHistogram.computeIfAbsent(providerClass,
|
||||
{ FusHistogramBuilder(CodeVisionFusCollector.HISTOGRAM_BUCKETS) })
|
||||
synchronized(fusHistogramBuilder) {
|
||||
fusHistogramBuilder.addValue(durationMs)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun flush(): CodeVisionFusData {
|
||||
val providerClassToHistogram = providerClassToHistogram.mapValues {
|
||||
synchronized(it.value) {
|
||||
it.value.build().buckets
|
||||
}
|
||||
}
|
||||
return CodeVisionFusData(language, providerClassToHistogram)
|
||||
}
|
||||
}
|
||||
|
||||
internal class CodeVisionFusData(
|
||||
val language: Language,
|
||||
val providerClassToHistogram: Map<Class<*>, IntArray>,
|
||||
)
|
||||
@@ -72,10 +72,10 @@ class CodeVisionPass(
|
||||
span.useWithScope {
|
||||
computeWithSpan(tracer, provider.javaClass.simpleName) {
|
||||
val results: List<Pair<TextRange, CodeVisionEntry>>
|
||||
//val duration = measureTimeMillis {
|
||||
val duration = measureTimeMillis {
|
||||
results = provider.computeForEditor(editor, file)
|
||||
//}
|
||||
//CodeVisionFusCollector.CODE_VISION_FINISHED.log(file.project, duration, provider::class.java, file.language)
|
||||
}
|
||||
CodeVisionFusCollector.reportCodeVisionProviderDuration(editor, file.language, duration, provider::class.java)
|
||||
providerIdToLenses[provider.id] = DaemonBoundCodeVisionCacheService.CodeVisionWithStamp(results,
|
||||
modificationTracker.modificationCount)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.hints.codeVision
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
|
||||
@Internal
|
||||
class FusHistogramBuilder(private val bucketValues: LongArray) {
|
||||
val buckets: IntArray = IntArray(bucketValues.size)
|
||||
|
||||
fun addValue(value: Long) {
|
||||
var bucketIndex = bucketValues.indexOfFirst { it > value }
|
||||
if (bucketIndex != -1) {
|
||||
// We found a bucket that needs to be incremented
|
||||
if (bucketIndex > 0) {
|
||||
// The value falls into the previous bucket
|
||||
bucketIndex--
|
||||
}
|
||||
} else {
|
||||
// The value is greater than all buckets, it falls into the last one
|
||||
bucketIndex = bucketValues.size - 1
|
||||
}
|
||||
buckets[bucketIndex]++
|
||||
}
|
||||
|
||||
fun build(): FusHistogram {
|
||||
return FusHistogram(buckets)
|
||||
}
|
||||
}
|
||||
|
||||
@Internal
|
||||
class FusHistogram(
|
||||
val buckets: IntArray
|
||||
)
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.hints
|
||||
|
||||
import com.intellij.codeInsight.hints.codeVision.FusHistogramBuilder
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class FusHistogramTest {
|
||||
@Test
|
||||
fun test() {
|
||||
testHist_0_10_50(1, 0, 0) {
|
||||
addValue(1)
|
||||
}
|
||||
testHist_0_10_50(0, 1, 0) {
|
||||
addValue(14)
|
||||
}
|
||||
testHist_0_10_50(0, 0, 1) {
|
||||
addValue(50)
|
||||
}
|
||||
testHist_0_10_50(0, 0, 1) {
|
||||
addValue(100)
|
||||
}
|
||||
testHist_0_10_50(1, 2, 1) {
|
||||
addValue(100)
|
||||
addValue(15)
|
||||
addValue(5)
|
||||
addValue(12)
|
||||
}
|
||||
// this situation (out of range) should not happen, it is UB
|
||||
testHist_0_10_50(1, 0, 0) {
|
||||
addValue(-10)
|
||||
}
|
||||
}
|
||||
|
||||
fun testHist_0_10_50(vararg buckets: Int, b: FusHistogramBuilder.() -> Unit) {
|
||||
val builder = FusHistogramBuilder(longArrayOf(0, 10, 50))
|
||||
b(builder)
|
||||
Assertions.assertArrayEquals(buckets, builder.build().buckets)
|
||||
}
|
||||
}
|
||||
@@ -1129,6 +1129,7 @@
|
||||
<statistics.counterUsagesCollector implementationClass="com.intellij.analysis.problemsView.toolWindow.ProblemsViewStatsCollector"/>
|
||||
<statistics.counterUsagesCollector implementationClass="com.intellij.ide.util.FileStructurePopupTimeTracker"/>
|
||||
<statistics.counterUsagesCollector implementationClass="com.intellij.codeInsight.hints.codeVision.CodeVisionFusCollector"/>
|
||||
<editorFactoryListener implementation="com.intellij.codeInsight.hints.codeVision.CodeVisionFusEditorListener" />
|
||||
|
||||
<projectViewPane implementation="com.intellij.ide.scopeView.ScopeViewPane"/>
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.intellij.codeInsight.hints.codeVision.CodeVisionFusCollector
|
||||
import com.intellij.codeInsight.hints.isCodeAuthorInlayHintsEnabled
|
||||
import com.intellij.codeInsight.hints.refreshCodeAuthorInlayHints
|
||||
import com.intellij.ide.PowerSaveMode
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.runInEdt
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.Service.Level
|
||||
@@ -28,9 +29,12 @@ import com.intellij.openapi.vcs.VcsException
|
||||
import com.intellij.openapi.vcs.VcsListener
|
||||
import com.intellij.openapi.vcs.changes.ChangeListManager
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.util.concurrency.AppExecutorUtil
|
||||
import com.intellij.util.ui.update.DisposableUpdate
|
||||
import com.intellij.util.ui.update.MergingUpdateQueue
|
||||
import com.intellij.vcs.CacheableAnnotationProvider
|
||||
import kotlinx.coroutines.Runnable
|
||||
|
||||
@Service(Level.PROJECT)
|
||||
internal class AnnotationsPreloader(private val project: Project) {
|
||||
@@ -53,7 +57,16 @@ internal class AnnotationsPreloader(private val project: Project) {
|
||||
|
||||
annotationProvider.populateCache(file)
|
||||
val durationMs = System.currentTimeMillis() - start
|
||||
//CodeVisionFusCollector.ANNOTATION_LOADED.log(project, durationMs)
|
||||
|
||||
AppExecutorUtil.getAppExecutorService().submit(Runnable {
|
||||
ApplicationManager.getApplication().runReadAction {
|
||||
val psiFile = PsiManager.getInstance(project).findFile(file)
|
||||
if (psiFile != null) {
|
||||
CodeVisionFusCollector.reportVcsAnnotationDuration(psiFile, durationMs)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
LOG.debug { "Preloaded VCS annotations for ${file.name} in $durationMs ms" }
|
||||
|
||||
runInEdt {
|
||||
|
||||
Reference in New Issue
Block a user