# 性能优化：700Token并发100优化方案 v3.11.9

## 📋 文档时间
2025-10-08

## 🎯 场景说明

**实际需求：**
- 总token数：**700+个**
- 并发执行：**100个同时运行**
- 总批次：**7批**（700 ÷ 100 = 7）
- 关键目标：**降低CPU和内存开销**

## 📊 当前问题分析

### 性能瓶颈

#### 1️⃣ 内存开销（最严重）
```javascript
// 100个并发token的内存占用估算：
100个WebSocket连接：        ~500-1000MB
100个token状态对象：         ~50-100MB
100个Vue响应式代理：         ~100-200MB
100个UI卡片DOM：            ~200-300MB
执行进度数据：               ~50-100MB
日志数据：                   ~100-200MB
------------------------------------------
预估总内存：                 1000-1900MB（1-2GB）⚠️

// 加上浏览器基础占用（约500MB）
总计：                       1.5-2.5GB
```

**问题：** 浏览器可能崩溃或极度卡顿

#### 2️⃣ CPU开销
```javascript
// CPU占用来源：
1. 100个WebSocket消息处理    ← 主要
2. 100个Vue响应式更新         ← 主要
3. 100个UI卡片渲染            ← 主要
4. 日志输出（console.log）    ← 次要
5. 任务进度计算               ← 次要
6. CSS动画                    ← 次要

预估CPU占用：40-60% （多核情况下）⚠️
```

#### 3️⃣ UI渲染压力
```javascript
// 100个token卡片同时更新：
每秒更新次数：100个 × 2次/秒 = 200次/秒
触发重排/重绘：频繁
滚动性能：卡顿
动画效果：掉帧

结果：界面卡顿严重 ⚠️
```

---

## ✅ 优化方案（七大维度）

### 🎯 方案1：虚拟滚动（最重要！）

#### 问题
```
100个token卡片全部渲染在DOM中
→ 大量DOM节点
→ 内存和渲染压力巨大
```

#### 解决方案
```javascript
// 实现虚拟滚动，只渲染可见区域
屏幕可见：       约15-20个卡片
预加载：         上下各5个
实际渲染：       约25-30个卡片

内存节省：       70%（100个 → 30个）
渲染性能提升：   3-5倍
```

#### 实现建议
```javascript
// 使用 vue-virtual-scroller 或 vue-virtual-scroll-list
// 或自己实现简单版本

// 核心思路：
1. 计算可见区域（viewport）
2. 只渲染可见+预加载的卡片
3. 监听滚动，动态更新渲染列表
4. 使用 transform 而非 top/margin 定位
```

**预期效果：**
- 内存：1.5GB → **800MB** ⬇️ 减少47%
- CPU：50% → **25%** ⬇️ 减少50%
- 渲染：卡顿 → **流畅** ✅

---

### 🔇 方案2：禁用/优化日志输出

#### 问题
```javascript
// 100个token × 每秒10条日志 = 1000条/秒
console.log() 非常消耗性能
每条日志都会触发浏览器渲染
```

#### 解决方案
```javascript
// 方案A：生产模式禁用日志（推荐）
const DEBUG_MODE = false  // 批量任务时设为false

if (DEBUG_MODE) {
  console.log(...)
}

// 方案B：限制日志级别
只保留 error 和 warn
禁用 info 和 debug

// 方案C：批量输出日志
每100条日志合并输出一次
而不是每条都立即输出
```

**预期效果：**
- CPU：-10-15%
- 渲染流畅度：明显提升

---

### ⚡ 方案3：UI更新节流

#### 问题
```javascript
// 100个token每500ms更新一次进度
// 每秒触发200次Vue响应式更新
taskProgress.value[tokenId].progress = 50  // 触发更新
```

#### 解决方案
```javascript
// 使用节流（throttle）限制更新频率

// 当前：每个任务完成就更新
// 优化：每1-2秒批量更新一次

const updateBatch = new Map()
let updateTimer = null

function throttledUpdate(tokenId, updates) {
  updateBatch.set(tokenId, { ...updateBatch.get(tokenId), ...updates })
  
  if (!updateTimer) {
    updateTimer = setTimeout(() => {
      // 批量应用所有更新
      updateBatch.forEach((updates, id) => {
        Object.assign(taskProgress.value[id], updates)
      })
      updateBatch.clear()
      updateTimer = null
    }, 1000)  // 每秒更新一次
  }
}
```

**预期效果：**
- Vue响应式更新：200次/秒 → **20次/秒** ⬇️
- CPU：-5-10%

---

### 🎨 方案4：禁用动画和过渡

#### 问题
```javascript
// CSS动画和过渡效果消耗GPU/CPU
.card {
  transition: all 0.3s;
  animation: pulse 2s infinite;
}
```

#### 解决方案
```javascript
// 在批量任务执行时禁用所有动画

// 方案A：添加CSS类
.batch-executing {
  * {
    animation: none !important;
    transition: none !important;
  }
}

// 方案B：JavaScript控制
document.body.classList.add('disable-animations')
```

**预期效果：**
- GPU占用：-20-30%
- 页面流畅度：提升

---

### 💾 方案5：优化内存管理

#### 5.1 限制历史记录

```javascript
// 问题：保留所有执行历史
executionHistory.value.unshift(historyItem)

// 优化：只保留最近的
if (executionHistory.value.length > 5) {  // 改为5
  executionHistory.value = executionHistory.value.slice(0, 5)
}
```

#### 5.2 清理已完成的token数据

```javascript
// 问题：已完成的token仍保留完整数据
// 优化：只保留摘要信息

function compactCompletedToken(tokenId) {
  const progress = taskProgress.value[tokenId]
  if (progress.status === 'completed' || progress.status === 'failed') {
    // 只保留关键信息，清理详细数据
    progress.result = null  // 清理任务结果详情
    progress.error = progress.error ? '错误已记录' : null
  }
}
```

#### 5.3 WebSocket连接及时断开

```javascript
// 确保任务完成后立即断开连接
// 当前已实现，但要确保没有泄漏

finally {
  if (tokenStore.wsConnections[tokenId]) {
    tokenStore.closeWebSocketConnection(tokenId)
  }
}
```

**预期效果：**
- 内存占用：-200-400MB

---

### 🔧 方案6：优化WebSocket消息处理

#### 问题
```javascript
// 每条消息都触发复杂的响应式更新
socket.onmessage = (event) => {
  const data = parseMessage(event.data)
  // 触发多层响应式更新
  store.messages.push(data)  // 触发更新
  store.unreadCount++        // 触发更新
  updateUI()                 // 触发更新
}
```

#### 解决方案
```javascript
// 使用 nextTick 批量处理消息
const messageQueue = []

socket.onmessage = (event) => {
  messageQueue.push(event.data)
  
  // 使用 requestIdleCallback 在空闲时处理
  requestIdleCallback(() => {
    const batch = messageQueue.splice(0, 100)
    processBatch(batch)
  })
}
```

**预期效果：**
- 消息处理效率：提升30-50%
- CPU尖刺：减少

---

### ⏱️ 方案7：进一步缩短延迟时间

#### 当前配置
```javascript
连接稳定等待: 2000ms
任务间隔: 500ms
连接间隔: 500ms
```

#### 优化配置（100并发专用）
```javascript
连接稳定等待: 300ms   ⬇️（并发高时可以更短）
任务间隔: 200ms        ⬇️
连接间隔: 300ms        ⬇️
子任务延迟: 50-100ms   ⬇️
```

**原理：**
- 100个并发时，等待时间可以重叠
- 不需要每个都等足2秒
- 通过并发实现"虚拟等待"

**预期效果：**
- 每个token节省：3-5秒
- 总时间节省：100个 × 4秒 = 400秒

---

## 🔧 具体代码实施

### 优先级1：立即实施（核心优化）

#### ✅ 1. 禁用日志输出

在 `src/stores/batchTaskStore.js` 顶部添加：

```javascript
// 批量任务性能优化：禁用日志
const ENABLE_BATCH_LOGS = false  // 改为false

// 包装所有console.log
const batchLog = ENABLE_BATCH_LOGS ? console.log.bind(console) : () => {}

// 替换所有 console.log 为 batchLog
// 搜索：console.log
// 替换：batchLog
```

#### ✅ 2. 缩短延迟时间

```javascript
// 第348行：连接稳定等待
await new Promise(resolve => setTimeout(resolve, 2000))
// 改为
await new Promise(resolve => setTimeout(resolve, 300))

// 第401行：任务间隔
await new Promise(resolve => setTimeout(resolve, 500))
// 改为
await new Promise(resolve => setTimeout(resolve, 200))

// 第259行：连接间隔
const delayMs = connectionIndex * 500
// 改为
const delayMs = connectionIndex * 300
```

#### ✅ 3. 限制历史记录

```javascript
// 第1689行
if (executionHistory.value.length > 10) {
  executionHistory.value = executionHistory.value.slice(0, 10)
}
// 改为
if (executionHistory.value.length > 3) {
  executionHistory.value = executionHistory.value.slice(0, 3)
}
```

#### ✅ 4. UI更新节流

在 `src/stores/batchTaskStore.js` 中添加：

```javascript
// 更新进度节流
let updateThrottleTimer = null
const pendingUpdates = new Map()

const updateTaskProgressThrottled = (tokenId, updates) => {
  pendingUpdates.set(tokenId, { ...pendingUpdates.get(tokenId), ...updates })
  
  if (!updateThrottleTimer) {
    updateThrottleTimer = setTimeout(() => {
      pendingUpdates.forEach((updates, id) => {
        if (taskProgress.value[id]) {
          Object.assign(taskProgress.value[id], updates)
        }
      })
      pendingUpdates.clear()
      updateThrottleTimer = null
    }, 500)  // 每500ms更新一次
  }
}

// 在非关键更新处使用 updateTaskProgressThrottled
// 关键更新（开始、结束、失败）仍使用 updateTaskProgress
```

---

### 优先级2：重要优化（需要更多工作）

#### ⭐ 虚拟滚动实现

这需要修改 `TaskProgressCard` 组件的渲染逻辑。

**简单方案：**
```vue
<template>
  <div class="token-list-container" @scroll="handleScroll">
    <div class="token-list-spacer" :style="{ height: totalHeight + 'px' }">
      <div class="token-list-visible" :style="{ transform: `translateY(${offsetY}px)` }">
        <TaskProgressCard
          v-for="token in visibleTokens"
          :key="token.id"
          :token="token"
        />
      </div>
    </div>
  </div>
</template>

<script setup>
const CARD_HEIGHT = 200  // 每个卡片高度
const VISIBLE_COUNT = 20  // 可见数量
const BUFFER_COUNT = 5    // 缓冲数量

const visibleTokens = computed(() => {
  const start = Math.max(0, scrollTop.value - BUFFER_COUNT)
  const end = Math.min(tokens.length, start + VISIBLE_COUNT + BUFFER_COUNT * 2)
  return tokens.slice(start, end)
})

const totalHeight = computed(() => tokens.length * CARD_HEIGHT)
const offsetY = computed(() => scrollTop.value * CARD_HEIGHT)
</script>
```

**预期效果：**
- 内存：-800MB
- CPU：-25%
- 渲染：流畅

---

## 📊 优化效果预估

### 内存占用对比

| 项目 | 优化前 | 优化后 | 节省 |
|------|--------|--------|------|
| **DOM渲染** | 1000MB | 300MB | **-700MB** ⬇️ |
| **日志数据** | 200MB | 20MB | **-180MB** ⬇️ |
| **历史记录** | 100MB | 30MB | **-70MB** ⬇️ |
| **其他** | 500MB | 450MB | -50MB |
| **总计** | **1800MB** | **800MB** | **-1000MB (55%)** ⬇️ |

### CPU占用对比

| 项目 | 优化前 | 优化后 | 节省 |
|------|--------|--------|------|
| **UI渲染** | 20% | 5% | **-15%** ⬇️ |
| **日志输出** | 10% | 1% | **-9%** ⬇️ |
| **Vue更新** | 15% | 5% | **-10%** ⬇️ |
| **WS消息** | 10% | 8% | -2% |
| **任务逻辑** | 5% | 5% | 0% |
| **总计** | **60%** | **24%** | **-36% (60%)** ⬇️ |

### 执行时间对比（700个token）

| 方案 | 批次 | 单批时间 | 总时间 |
|------|------|---------|--------|
| **优化前** | 7批 | 3-4分钟 | **21-28分钟** |
| **优化后** | 7批 | 1.5-2分钟 | **10.5-14分钟** ⬇️ |
| **节省** | - | 1.5-2分钟 | **10-14分钟 (50%)** ⬇️ |

---

## ⚠️ 实施建议

### 分阶段实施

#### 阶段1：立即可做（30分钟）
```
1. ✅ 禁用日志输出
2. ✅ 缩短延迟时间
3. ✅ 限制历史记录
4. ✅ 测试50个token

预期效果：内存-300MB，CPU-15%
```

#### 阶段2：重要优化（2小时）
```
1. ⭐ 实现虚拟滚动
2. ✅ UI更新节流
3. ✅ 禁用动画
4. ✅ 测试100个token

预期效果：内存-800MB，CPU-35%
```

#### 阶段3：全面测试（1天）
```
1. 测试100并发
2. 测试700全量（7批）
3. 监控性能指标
4. 调优参数
```

---

### 测试检查清单

**启动前检查：**
- [ ] 关闭其他浏览器标签页
- [ ] 关闭不必要的应用程序
- [ ] 确保内存 >4GB 可用
- [ ] 确保网络稳定

**运行中监控：**
- [ ] 任务管理器监控内存（目标<2GB）
- [ ] 任务管理器监控CPU（目标<40%）
- [ ] 浏览器控制台无错误
- [ ] 页面可以正常滚动

**完成后检查：**
- [ ] 成功率 >85%
- [ ] 浏览器未崩溃
- [ ] 内存正常释放
- [ ] 记录实际数据

---

## 💡 额外建议

### 1. 分批策略优化

**不建议：** 一次100个 × 7批
**建议：** 
```
方案A：50个 × 14批（更稳定）
方案B：70个 × 10批（平衡）
方案C：100个 × 7批（激进，需全面优化）
```

### 2. 时间分散

**建议：** 
```
- 不要一口气跑完700个
- 分成早中晚3次，每次跑230个
- 避免长时间高负载
```

### 3. 备用方案

如果浏览器仍然吃不消：

**方案A：使用多个浏览器实例**
```
浏览器1：运行350个token（并发50）
浏览器2：运行350个token（并发50）
```

**方案B：使用Headless模式**
```
使用Puppeteer/Playwright
无UI运行，内存占用更小
```

---

## 📝 需要我立即实施的代码修改

我可以马上帮您修改：

### ✅ 核心优化（推荐立即实施）
1. 禁用日志输出
2. 缩短延迟时间（300ms/200ms/300ms）
3. 限制历史记录（10条→3条）
4. UI更新节流（每500ms）

**预期效果：**
- 内存：1800MB → **1200MB** ⬇️
- CPU：60% → **35%** ⬇️
- 时间：21分钟 → **14分钟** ⬇️

### ⭐ 重要优化（需要更多时间）
5. 虚拟滚动（需要1-2小时）
6. 禁用动画
7. WebSocket消息批处理

**预期效果：**
- 内存：1200MB → **800MB** ⬇️
- CPU：35% → **24%** ⬇️
- 时间：14分钟 → **10.5分钟** ⬇️

---

## 🚀 请确认

**您希望我现在：**
1. ✅ 立即实施核心优化（4项修改）？
2. ⭐ 同时实施虚拟滚动（需要更多时间）？
3. 📋 还是先提供完整修改清单，您自行决定？

**或者您想先测试：**
- 调整并发数到50或70，看看现状能否承受？

请告诉我您的选择！ 🎯

