1300个群组50个机器人的循环迭代运行耗时10分钟,请问性能缓慢原因是什么?
性能问题根因
你的代码核心问题是O(n²)甚至更高的时间复杂度,1300个群组、50个机器人的场景下总共有65000次内外层循环迭代,每次迭代中反复执行find/filter这类O(n)操作,且随着scheduled_joins_total数组不断变长,后续遍历成本会越来越高,最终运行时间被指数级放大。
具体优化方案
1. 提前构建全局索引,将查找操作降为O(1)
所有需要反复查找的数据集,在循环开始前一次性处理成哈希结构:
- 将
answers中的GroupId存入Set,直接判断群组是否需要处理 - 将
bots_joined_groups处理为Map:key为botId,value为该bot已加入群组ID的Set,无需每次遍历查找 - 将
scheduled_joins_total预分组:按botId分组存储调度记录,同时构建日期+groupId的全局Set,用于快速判断某群组当天是否已有调度 - 所有日期提前转为
YYYYMMDD格式的字符串,避免反复调用getDate/getMonth/getFullYear做日期对比
2. 提取循环外的常量与重复计算
- 把
day = 86400000这类常量提到循环最外层,无需每次迭代重新计算 - 全局统一计算当前日期,不要每次迭代都新建
Date()对象 - 提前过滤掉禁用的bot,减少内层循环次数
3. 优化调度计数逻辑
提前统计每个bot每日的调度次数,不要每次查找时遍历全部调度记录计算
优化后代码示例
// 提前预处理所有索引 const ANSWER_GROUP_SET = new Set(answers.map(a => a.GroupId)); const BOT_JOINED_GROUP_MAP = new Map( bots_joined_groups.map(j => [j.Id, new Set(j.Groups.map(g => g.GroupId))]) ); // 预分组bot调度记录,同时构建日期-group的索引 const BOT_SCHEDULE_MAP = new Map(); const DATE_GROUP_SET = new Set(); const day = 86400000; const now = new Date(); const getDateStr = (d) => `${d.getFullYear()}${String(d.getMonth()+1).padStart(2,'0')}${String(d.getDate()).padStart(2,'0')}`; // 初始化调度数据索引 scheduled_joins_total.forEach(item => { const dateStr = getDateStr(new Date(item.At)); if (!BOT_SCHEDULE_MAP.has(item.BotId)) { BOT_SCHEDULE_MAP.set(item.BotId, { records: [], dayCount: new Map() }); } const botData = BOT_SCHEDULE_MAP.get(item.BotId); botData.records.push(item); botData.dayCount.set(dateStr, (botData.dayCount.get(dateStr) || 0) + 1); DATE_GROUP_SET.add(`${dateStr}_${item.GroupId}`); }); // 提前过滤可用bot const availableBots = bots.filter(b => !b.Disabled); for (const group of groups) { // O(1)判断群组是否需要处理 if (!ANSWER_GROUP_SET.has(group.Id)) continue; for (const bot of availableBots) { // O(1)判断bot是否已加入该群 const joinedGroups = BOT_JOINED_GROUP_MAP.get(bot.Id) || new Set(); if (joinedGroups.has(group.Id)) continue; // 计算基准日期 let today = new Date(now); const warmEnd = new Date(bot.WarmingUpEndAt); if (today < warmEnd) { today = warmEnd; } else { const limitAt = new Date(bot.LimitAt); if (Math.floor((today.getTime() - limitAt.getTime()) / day) < 7) { today = new Date(limitAt.getTime() + 7 * day); } } const todayStr = getDateStr(today); // 计算调度起始日期 let scheduleDate = new Date(today); const botSchedule = BOT_SCHEDULE_MAP.get(bot.Id) || {records: [], dayCount: new Map()}; if (botSchedule.records.length) { // 直接从预统计的日计数里找符合要求的日期 let foundDate = null; for (const [dateStr, count] of botSchedule.dayCount.entries()) { if (count < 10) { foundDate = new Date(dateStr.slice(0,4), parseInt(dateStr.slice(4,6))-1, parseInt(dateStr.slice(6,8))); break; } } if (foundDate) { scheduleDate = foundDate; } else { // 取最后一条记录加一天 const lastRecord = botSchedule.records[botSchedule.records.length-1]; scheduleDate = new Date(new Date(lastRecord.At).getTime() + day); } } const scheduleDateStr = getDateStr(scheduleDate); // 计算最小时分 let minHours = 0, minMinutes = 0; if (todayStr === scheduleDateStr) { // 统计当天已加入数量 let joinedTodayCount = 0; const joinedGroups = BOT_JOINED_GROUP_MAP.get(bot.Id) || []; for (const g of joinedGroups) { if (getDateStr(new Date(g.JoinedAt)) === todayStr) joinedTodayCount++; } if (joinedTodayCount >= 10 || today.getHours() >= 23) { // 跳到下一天 if (scheduled_joins_total.length) { const lastGlobal = scheduled_joins_total[scheduled_joins_total.length-1]; scheduleDate = new Date(new Date(lastGlobal.At).getTime() + day); } else { scheduleDate = new Date(today.getTime() + day); } minHours = 0; minMinutes = 0; } else { if (scheduleDate.getHours() <= today.getHours()) { minHours = today.getHours() + 1; } } } // 随机时间 scheduleDate.setHours(Math.random() * (23 - minHours + 1) + minHours); scheduleDate.setMinutes(Math.random() * 59 + minMinutes); scheduleDate.setSeconds(Math.random() * 59); // O(1)判断该日期+群组是否已有调度 let finalDateStr = getDateStr(scheduleDate); while (DATE_GROUP_SET.has(`${finalDateStr}_${group.Id}`)) { scheduleDate.setTime(scheduleDate.getTime() + day); finalDateStr = getDateStr(scheduleDate); } // 更新索引与结果 const scheduleItem = { BotId: bot.Id, GroupId: group.Id, At: scheduleDate }; scheduled_joins_total.push(scheduleItem); // 更新bot调度索引 if (!BOT_SCHEDULE_MAP.has(bot.Id)) { BOT_SCHEDULE_MAP.set(bot.Id, {records: [], dayCount: new Map()}); } const botData = BOT_SCHEDULE_MAP.get(bot.Id); botData.records.push(scheduleItem); botData.dayCount.set(finalDateStr, (botData.dayCount.get(finalDateStr) || 0) + 1); DATE_GROUP_SET.add(`${finalDateStr}_${group.Id}`); values.push(`(${bot.Id}, ${group.Id}, to_timestamp(${scheduleDate.getTime() / 1000.0}))`); } }
优化后整体运行时间可降到秒级。
内容的提问来源于stack exchange,提问作者Mattia Bonfanti
相关产品推荐
相关产品推荐

