从零开始搞监控系统(3)——性能监控

2023-10-27

  前端性能监控是个老话题了,各个团队都会对其有所关注,因为关注性能是工程师的本分。

  页面性能对用户体验而言十分关键,每次重构或优化,仅靠手中的几个设备或模拟的测试,缺少说服力,需要有大量的真实数据来做验证。

  这次不同,公司急切的需要一套性能监控系统,用于分析线上的活动,要扎扎实实的提升用户体验。

  整个系统大致的运行流程如下:

  

一、SDK

  性能参数搜集的代码仍然写在前面的监控 shin.js(SDK) 中,为了兼容两个版本的性能标准,专门编写了一个函数。

function _getTiming() {
  var timing =
    performance.getEntriesByType("navigation")[0] || performance.timing;
  var now = 0;
  if (!timing) {
    return { now: now };
  }
  var navigationStart;
  if (timing.startTime === undefined) {
    navigationStart = timing.navigationStart;
    /**
     * 之所以老版本的用 Date,是为了防止出现负数
     * 当 performance.now 是最新版本时,数值的位数要比 timing 中的少很多
     */
    now = new Date().getTime() - navigationStart;
  } else {
    navigationStart = timing.startTime;
    now = shin.now() - navigationStart;
  }
  return {
    timing: timing,
    navigationStart: navigationStart,
    now: _rounded(now)
  };
}

  其实两种方式得当的参数类似,第二版中的参数比第一版来的多,下面两张图是官方给的参数示意图,粗看的话下面两种差不多。

W3C第一版的性能参数

W3C第二版的性能检测参数

  但其实在将 performance.getEntriesByType("navigation")[0] 打印出来后,就会发现它还会包含页面地址、传输的数据量、协议等字段。

1)统计的参数

  网上有很多种统计性能参数的计算方式,大部分都差不多,我选取了其中较为常规的参数。

shin.getTimes = function () {
  //出于对浏览器兼容性的考虑,仍然引入即将淘汰的 performance.timing
  var currentTiming = _getTiming();
  var timing = currentTiming.timing;
  var api = {};     //时间单位 ms
  if (!timing) {
    return api;
  }
  var navigationStart = currentTiming.navigationStart;
  /**
   * 页面加载总时间
   * 这几乎代表了用户等待页面可用的时间
   * loadEventEnd(加载结束)-navigationStart(导航开始)
   */
  api.loadTime = timing.loadEventEnd - navigationStart;
  /**
   * Unload事件耗时
   */
  api.unloadEventTime = timing.unloadEventEnd - timing.unloadEventStart;
  /**
   * 执行 onload 回调函数的时间
   * 是否太多不必要的操作都放到 onload 回调函数里执行了,考虑过延迟加载、按需加载的策略么?
   */
  api.loadEventTime = timing.loadEventEnd - timing.loadEventStart;
  /**
   * 首次可交互时间
   */
  api.interactiveTime = timing.domInteractive - timing.fetchStart;
  /**
   * 用户可操作时间(DOM Ready时间)
   */
  api.domReadyTime = timing.domContentLoadedEventEnd - timing.fetchStart;
  /**
   * 白屏时间
   */
  var paint = performance.getEntriesByType("paint");
  if (paint && timing.entryType && paint[0]) {
    api.firstPaint = paint[0].startTime - timing.fetchStart;
  } else {
    api.firstPaint = timing.responseEnd - timing.fetchStart;
  }
  /**
   * 解析 DOM 树结构的时间
   * 期间要加载内嵌资源
   * 反省下你的 DOM 树嵌套是不是太多了
   */
  api.parseDomTime = timing.domComplete - timing.domInteractive;
  /**
   * 请求完毕至DOM加载耗时
   */
  api.initDomTreeTime = timing.domInteractive - timing.responseEnd;
  /**
   * 准备新页面耗时
   */
  api.readyStart = timing.fetchStart - navigationStart;
  /**
   * 重定向次数(新)
   */
  api.redirectCount = timing.redirectCount || 0;
  /**
   * 传输内容压缩百分比(新)
   */
  api.compression = (1 - timing.encodedBodySize / timing.decodedBodySize) * 100 || 0;
  /**
   * 重定向的时间
   * 拒绝重定向!比如,http://example.com/ 就不该写成 http://example.com
   */
  api.redirectTime = timing.redirectEnd - timing.redirectStart;
  /**
   * DNS缓存耗时
   */
  api.appcacheTime = timing.domainLookupStart - timing.fetchStart;
  /**
   * DNS查询耗时
   * DNS 预加载做了么?页面内是不是使用了太多不同的域名导致域名查询的时间太长?
   * 可使用 HTML5 Prefetch 预查询 DNS 
   */
  api.lookupDomainTime = timing.domainLookupEnd - timing.domainLookupStart;
  /**
   * SSL连接耗时
   */
  var sslTime = timing.secureConnectionStart;
  api.connectSslTime = sslTime > 0 ? timing.connectEnd - sslTime : 0;
  /**
   * TCP连接耗时
   */
  api.connectTime = timing.connectEnd - timing.connectStart;
  /**
   * 内容加载完成的时间
   * 页面内容经过 gzip 压缩了么,静态资源 css/js 等压缩了么?
   */
  api.requestTime = timing.responseEnd - timing.requestStart;
  /**
   * 请求文档
   * 开始请求文档到开始接收文档
   */
  api.requestDocumentTime = timing.responseStart - timing.requestStart;
  /**
   * 接收文档(内容传输耗时)
   * 开始接收文档到文档接收完成
   */
  api.responseDocumentTime = timing.responseEnd - timing.responseStart;
  /**
   * 读取页面第一个字节的时间
   * 这可以理解为用户拿到你的资源占用的时间,加异地机房了么,加CDN 处理了么?加带宽了么?加 CPU 运算速度了么?
   * TTFB 即 Time To First Byte 的意思
   * 维基百科:https://en.wikipedia.org/wiki/Time_To_First_Byte
   */
  api.TTFB = timing.responseStart - timing.fetchStart;
  //全部取整
  for (var key in api) {
    api[key] = _rounded(api[key]);
  }
  /**
   * 浏览器读取到的性能参数,用于排查
   */
  api.timing = timing;
  return api;
};

  所有的性能参数最终都要被取整,以毫秒作单位。兼容的 timing 对象也会被整个传递到后台,便于分析性能参数是怎么计算出来的。

  compression(传输内容压缩百分比)是一个新的参数。白屏时间的计算有两种:

  第一种是调用 performance.getEntriesByType("paint") 方法,再减去 fetchStart;第二种是用 responseEnd 来与 fetchStart 相减。

  loadTime(页面加载总时间)有可能为0,就是当页面资源还没加载完,触发 load 事件前将页面关闭。

  如果这种很多,那就很有可能页面被阻塞在某个位置,可能是接收时间过长、可能是DOM解析过长等。

  当这个页面加载时间超过了用户的心理承受范围时,就需要抽出时间来做各个方面的页面优化了。

2)首屏时间

  首屏时间很难计算,一般有几种计算方式。

  第一种是算出首屏页面中所有图片都加载完后的时间,这种方法难以覆盖所有场景,并且计算结果并不准。

/**
 * 计算首屏时间
 * 记录首屏图片的载入时间
 * 用户在没有滚动时候看到的内容渲染完成并且可以交互的时间
 */
doc.addEventListener(
  "DOMContentLoaded",
  function () {
    var isFindLastImg = false,
      allFirsrImgsLoaded = false,
      firstScreenImgs = [];
    //用一个定时器差值页面中的图像元素
    var interval = setInterval(function () {
      //如果自定义了 firstScreen 的值,就销毁定时器
      if (shin.firstScreen) {
        clearInterval(interval);
        return;
      }
      if (isFindLastImg) {
        allFirsrImgsLoaded = firstScreenImgs.every(function (img) {
          return img.complete;
        });
        //当所有的首屏图像都载入后,关闭定时器并记录首屏时间
        if (allFirsrImgsLoaded) {
          shin.firstScreen = _calcCurrentTime();
          clearInterval(interval);
        }
        return;
      }
      var imgs = doc.querySelectorAll("img");
      imgs = [].slice.call(imgs); //转换成数组
      //遍历页面中的图像
      imgs.forEach(function (img) {
        if (isFindLastImg) return;
        //当图像离顶部的距离超过屏幕宽度时,被认为找到了首屏的最后一张图
        var rect = img.getBoundingClientRect();
        if (rect.top + rect.height > firstScreenHeight) {
          isFindLastImg = true;
          return;
        }
        //若未超过,则认为图像在首屏中
        firstScreenImgs.push(img);
      });
    }, 0);
  },
  false
);

  第二种是自定义首屏时间,也就是自己来控制何时算首屏全部加载好了,这种方法相对来说要精确很多。

shin.setFirstScreen = function() {
  this.firstScreen = _calcCurrentTime();
}
/**
 * 计算当前时间与 fetchStart 之间的差值
 */
function _calcCurrentTime() {
  return _getTiming().now;
}
/**
 * 标记时间,单位毫秒
 */
shin.now = function () {
  return performance.now();
}

  之所以未用 Date.now() 是因为它会受系统程序执行阻塞的影响, 而performance.now() 的时间是以恒定速率递增的,不受系统时间的影响(系统时间可被人为或软件调整)。

  在页面关闭时还未获取到首屏时间,那么它就默认是 domReadyTime(用户可操作时间)。

3)上报

  本次上报与之前不同,需要在页面关闭时上报。而在此时普通的请求可能都无法发送成功,那么就需要 navigator.sendBeacon() 的帮忙了。

  它能将少量数据异步 POST 到后台,并且支持跨域,而少量是指多少并没有特别指明,由浏览器控制,网上查到的资料说一般在 64KB 左右。

  在接收数据时遇到个问题,由于后台使用的是 KOA 框架,解析请求数据使用了 koa-bodyparser 库,而它默认不会接收 Content-Type: text 的数据,因此要额外配置一下,具体可参考此处

/**
 * 在页面卸载之前,推送性能信息
 */
window.addEventListener("beforeunload", function () {
    var data = shin.getTimes();
    if (shin.param.rate > Math.random(0, 1) && shin.param.pkey) {
      navigator.sendBeacon(shin.param.psrc, _paramifyPerformance(data));
    }
  },
  false
);

  在上报时,还限定了一个采样率,默认只会把 50% 的性能数据上报到后台,并且必须定义 pkey 参数,这其实就是一个用于区分项目的 token。

  本来一切都是这么的顺利,但是在实际使用中发现,在 iOS 设备上调试发现不会触发 beforeunload 事件,安卓会将其触发,一番查找后,根据iOS支持的事件社区的解答,发现得用 pagehide 事件替代。

  以为万事大吉,但还是太年轻,在微信浏览器中的确能触发 pagehide 事件,但是在自己公司APP中,表现不尽如意,无法触发,若要监控关闭按钮,得发一次版本。

  无奈,只能自己想了个比较迂回的方法,那就是在后台跑个定时器,每 200ms 缓存一次要搜集的性能数据,在第二次进入时,再上报到后台。

/**
 * 组装性能变量
 */
function _paramifyPerformance(obj) {
  obj.token = shin.param.token;
  obj.pkey = shin.param.pkey;
  obj.identity = getIdentity();
  obj.referer = location.href; //来源地址
  // 若未定义或未计算到,则默认为用户可操作时间
  obj.firstScreen = shin.firstScreen || obj.domReadyTime;
  return JSON.stringify(obj);
}

/**
 * 均匀获得两个数字之间的随机数
 */
function _randomNum(max, min) {
  return Math.floor(Math.random() * (max - min + 1) + min);
}
/**
 * iOS 设备不支持 beforeunload 事件,需要使用 pagehide 事件
 * 在页面卸载之前,推送性能信息
 */
var isIOS = !!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
var eventName = isIOS ? "pagehide" : "beforeunload";
window.addEventListener(
  eventName,
  function () {
    sendBeacon();
  },
  false
);

var SHIN_PERFORMANCE_DATA = "shin_performance_data";
var heartbeat; //心跳定时器
/**
 * 发送数据
 */
function sendBeacon(existData) {
  // 如果传了数据就使用该数据,否则读取性能参数,并格式化为字符串
  var data = existData || _paramifyPerformance(shin.getTimes());
  var rate = _randomNum(10, 1); // 选取1~10之间的整数
  if (shin.param.rate >= rate && shin.param.pkey) {
    navigator.sendBeacon(shin.param.psrc, data);
  }
  clearTimeout(heartbeat);
  localStorage.removeItem(SHIN_PERFORMANCE_DATA); //移除性能缓存
}
/**
 * 发送已存在的性能数据
 */
function sendExistData() {
  var exist = localStorage.getItem(SHIN_PERFORMANCE_DATA);
  if (!exist) return;
  setTimeout(function () {
    sendBeacon(exist);
  }, 0);
}
sendExistData();
/**
 * 一个心跳回调函数,缓存性能参数
 * 适用于不能触发 pagehide 和 beforeunload 事件的浏览器
 */
function intervalHeartbeat() {
  localStorage.setItem(
    SHIN_PERFORMANCE_DATA,
    _paramifyPerformance(shin.getTimes())
  );
}
heartbeat = setInterval(intervalHeartbeat, 200);

二、存储

1)性能数据日志

  性能数据会被存储到 web_performance 表中,同样在接收时会通过队列来异步新增。

CREATE TABLE `web_performance` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `load` int(11) NOT NULL DEFAULT '0' COMMENT '页面加载总时间',
  `ready` int(11) NOT NULL DEFAULT '0' COMMENT '用户可操作时间',
  `paint` int(11) NOT NULL DEFAULT '0' COMMENT '白屏时间',
  `screen` int(11) NOT NULL DEFAULT '0' COMMENT '首屏时间',
  `measure` varchar(1000) COLLATE utf8mb4_bin NOT NULL COMMENT '其它测量参数,用JSON格式保存',
  `ctime` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `day` int(11) NOT NULL COMMENT '格式化的天(冗余字段),用于排序,20210322',
  `hour` tinyint(2) NOT NULL COMMENT '格式化的小时(冗余字段),用于分组,11',
  `minute` tinyint(2) DEFAULT NULL COMMENT '格式化的分钟(冗余字段),用于分组,20',
  `identity` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT '身份',
  `project` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT '项目关键字,关联 web_performance_project 表中的key',
  `ua` varchar(600) COLLATE utf8mb4_bin NOT NULL COMMENT '代理信息',
  `referer` varchar(200) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '来源地址',
  `timing` text COLLATE utf8mb4_bin COMMENT '浏览器读取到的性能参数,用于排查',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='性能监控';

  表中的 project 字段会关联 web_performance_project 表中的key。

2)性能项目

  性能项目就是要监控的页面,与之前不同,性能的监控粒度会更细,因此需要有个后台专门管理这类数据。

CREATE TABLE `web_performance_project` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `key` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT '唯一值',
  `name` varchar(45) COLLATE utf8mb4_bin NOT NULL COMMENT '项目名称',
  `ctime` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1:正常  0:删除',
  PRIMARY KEY (`id`),
  UNIQUE KEY `name_UNIQUE` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='性能监控项目';

  目前做的也比较简单,通过名称得到 16位MD5 字符串。 

  

三、分析

1)性能看板

  在性能看板中,会有四张折线图,当要统计一天的数据时,横坐标为小时(0~23),纵坐标为在这个小时内正序后处于 95% 位置的日志,也就是 95% 的用户打开页面的时间。

  这种写法也叫 TP95,TP 是 Top Percentile 的缩写,不用性能平均数是因为那么做不科学。

  

  过滤条件还可以选择具体的小时,此时横坐标为分钟,纵坐标为在这个分钟内正序后处于 95% 位置的日志。

  点击图表的 label 部分,可以在后面列表中显示日志细节,其中原始参数就是从浏览器中得到的计算前的性能数据。

  

  后面又增加了对比功能,就是将几天的数据放在一起对比,可更加直观的展示趋势。

  

2)定时任务

  在每天的凌晨 3点30 分,统计昨天的日志信息。可以就到第三方检测机构的信息。

  本来是计划 web_performance_statis 表中每天只有一条记录,所有性能项目的统计信息都塞到 statis 字段中,并且会包含各个对应的日志。

  但奈何数据量实在太大,超出了 MySQL 中 TEXT 类型的范围,没办法塞进去,后面就只存储 id 并且一个项目每天各一条记录。

  数据结构如下,其中 loadZero 是指未执行load事件的数量。

{
  hour: {
    x: [11, 14],
    load: ["158", "162"],
    ready: ["157", "162"],
    paint: ["158", "162"],
    screen: ["157", "162"],
    loadZero: 1
  },
  minute: {
    11: {
      x: [11, 18, 30],
      load: ["157", "159", "160"],
      ready: ["156", "159", "160"],
      paint: ["157", "159", "160"],
      screen: ["156", "159", "160"],
      loadZero: 1
    },
    14: {
      x: [9, 16, 17, 18],
      load: ["161", "163", "164", "165"],
      ready: ["161", "163", "164", "165"],
      paint: ["161", "163", "164", "165"],
      screen: ["161", "163", "164", "165"],
      loadZero: 0
    }
  }
}

  还有个定时任务会在每天的凌晨 4点30 分执行,将四周前的 web_performance_statis 和 web_performance 两张表中的数据清除。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从零开始搞监控系统(3)——性能监控 的相关文章

  • 软件测试|使用matplotlib绘制多种柱状图

    简介 在数据可视化领域 Matplotlib是一款强大的Python库 它可以用于创建多种类型的图表 包括柱状图 本文将介绍如何使用Matplotlib创建多种不同类型的柱状图 并提供示例代码 创建基本柱状图 首先 让我们创建一个基本的柱状
  • 【计算机毕业设计】电影播放平台

    电影播放平台采用B S架构 数据库是MySQL 网站的搭建与开发采用了先进的java进行编写 使用了springboot框架 该系统从两个对象 由管理员和用户来对系统进行设计构建 主要功能包括 个人信息修改 对用户 电影分类 电影信息等功能
  • Python自动化测试 | 如何使用Robot Framework进行自动化测试?

    2024软件测试面试刷题 这个小程序 永久刷题 靠它快速找到工作了 刷题APP的天花板 CSDN博客 文章浏览阅读2 3k次 点赞85次 收藏11次 你知不知道有这么一个软件测试面试的刷题小程序 里面包含了面试常问的软件测试基础题 web自
  • 【计算机毕业设计】OA公文发文管理系统_xtv98

    近年来 人们的生活方式以网络为主题不断进化 OA公文发文管理就是其中的一部分 现在 无论是大型的还是小型的网站 都随处可见 不知不觉中已经成为我们生活中不可或缺的存在 随着社会的发展 除了对系统的需求外 我们还要促进经济发展 提高工作效率
  • 【卡尔曼滤波】具有梯度流的一类系统的扩散映射卡尔曼滤波器研究(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码 数据 文章
  • 华为OD机试真题-分披萨-2023年OD统一考试(C卷)

    题目描述 吃货 和 馋嘴 两人到披萨店点了一份铁盘 圆形 披萨 并嘱咐店员将披萨按放射状切成大小相同的偶数扇形小块 但是粗心服务员将披萨切成了每块大小都完全不同奇数块 且肉眼能分辨出大小 由于两人都想吃到最多的披萨 他们商量了一个他们认为公
  • 华为OD机试2024年最新题库(Java)

    我是一名软件开发培训机构老师 我的学生已经有上百人通过了华为OD机试 学生们每次考完试 会把题目拿出来一起交流分享 重要 2024年1月 5月 考的都是OD统一考试 C卷 题库已经整理好了 命中率95 以上 这个专栏使用 Java 解法 问
  • 计算机Java项目|在线图书管理

    作者简介 Java领域优质创作者 CSDN博客专家 CSDN内容合伙人 掘金特邀作者 阿里云博客专家 51CTO特邀作者 多年架构师设计经验 腾讯课堂常驻讲师 主要内容 Java项目 Python项目 前端项目 人工智能与大数据 简历模板
  • 计算机Java项目|电影购票系统

    作者简介 Java领域优质创作者 CSDN博客专家 CSDN内容合伙人 掘金特邀作者 阿里云博客专家 51CTO特邀作者 多年架构师设计经验 腾讯课堂常驻讲师 主要内容 Java项目 Python项目 前端项目 人工智能与大数据 简历模板
  • 春眠不觉晓,Java数据类型知多少?基础牢不牢看完本文就有数了

    俺滴座右铭是不在沉默中爆发 就在沉默中灭亡 一起加油学习 珍惜现在来之不易的学习时光吧 等工作之后 你就会发现 想学习真的需要挤时间 厚积薄发啦 我们知道Java是面向对象的静态型编程语言 在Java的世界里万物皆对象 但我认为是万物皆数据
  • 最新整理Java面试八股文,大厂必备神器

    在此 我采访了数十名大厂的面试官和上百的的面试者 总结出了这一套Java面试八股文 这套八股文已经帮助了上百人拿到自己心仪的offer 我们先来看看这套八股文 Java基础面试八股文 操作系统中 heap 和 stack 的区别 什么是基于
  • 15天学会Python深度学习,我是如何办到的?

    陆陆续续有同学向我们咨询 Python编程如何上手 深度学习怎么学习 如果有人能手把手 一对一帮帮我就好了 我们非常理解初学者的茫然和困惑 大量视频 书籍 广告干扰了大家的判断 学习Python和人工智能 成为内行人不难 为此 我们推出了
  • 【心电图基线估计和去噪方法的群稀疏正则化】带有群稀疏正则化的心电图基线估计和去噪(Matlab实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码及文章
  • 2024年华为OD机试真题-靠谱的车-Python-OD统一考试(C卷)

    题目描述 程序员小明打了一辆出租车去上班 出于职业敏感 他注意到这辆出租车的计费表有点问题 总是偏大 出租车司机解释说他不喜欢数字4 所以改装了计费表 任何数字位置遇到数字4就直接跳过 其余功能都正常 比如 1 23再多一块钱就变为25 2
  • 为什么我强烈推荐大学生打CTF!

    前言 写这个文章是因为我很多粉丝都是学生 经常有人问 感觉大一第一个学期忙忙碌碌的过去了 啥都会一点 但是自己很难系统的学习到整个知识体系 很迷茫 想知道要如何高效学习 这篇文章我主要就围绕两点 减少那些罗里吧嗦的废话 直接上干货 CTF如
  • 手把手教你使用HarmonyOS本地模拟器

    我们通过下面的动图来回顾下手机本地模拟器的使用效果 本期 我们将为大家介绍HarmonyOS本地模拟器的版本演进 并手把手教大家使用HarmonyOS本地模拟器 一 本地模拟器的版本演进 2021年12月31日 经过一个版本的迭代优化 随D
  • 肿瘤的转录调控:Cell子刊揭示原发性肝癌中转录因子活性的全基因组图谱|国自然热点

    转录调控的研究历史比较长 相关研究在近十年来仍一直增长 也是近年来高分文章的焦点之一 在2023年最佳国自然 中标 研究热点 转录调控中标率高达189 作为国自然热点之一的肿瘤微环境的研究在近几年也一直处于上升趋势 转录调控在肿瘤发生 发展
  • 在 Python 中实现 List 抽象

    在 Python 中 创建一个包含多个对象的 list 很常见 例如 对于一组具有相同功能的对象 比如播放声音 希望能够使用类似 my list play 的语法来触发 list 中所有对象的 play 方法 另一个例子是 当希望关闭 li
  • 软件测试/测试开发|给你剖析闭包与装饰器的魔力

    测试管理班是专门面向测试与质量管理人员的一门课程 通过提升从业人员的团队管理 项目管理 绩效管理 沟通管理等方面的能力 使测试管理人员可以更好的带领团队 项目以及公司获得更快的成长 提供 1v1 私教指导 BAT 级别的测试管理大咖量身打造
  • 计算机Java项目|人体健康信息管理系统

    作者简介 Java领域优质创作者 CSDN博客专家 CSDN内容合伙人 掘金特邀作者 阿里云博客专家 51CTO特邀作者 多年架构师设计经验 腾讯课堂常驻讲师 主要内容 Java项目 Python项目 前端项目 人工智能与大数据 简历模板

随机推荐

  • std::numeric_limits 出错

    not enough actual parameters for macro max for std numeric limits
  • cuBLAS矩阵乘法性能分析(附代码示例)

    使用教程 矩阵乘法是神经网络中最基础 最重要的一个运算 在用CUDA实现矩阵乘法时 不需要我们手动写 cuBLAS库提供了现成的矩阵乘法算子 例如cublasGemmEx和cublasLtMatmul 其中后者是轻量级版本 API调用更灵活
  • DL入门(1):卷积神经网络(CNN)

    写在前面 看预测论文综述时 面临这样一个问题 很多DL的方法只是会简单运用 却不是特别了解其详细原理 故针对CNN RNN LSTM AutoEncoder RBM DBN以及DBM分别做一些简单总结 以达到了解的目的 此篇为CNN 目录
  • torch.stack()按轴叠加原理

    1 问题 大部分帖子和一些典型的介绍numpy pytorch的书籍对此部分并没有详细的介绍 仅仅简单地在np cat 或torch stack 等中提到当连接轴指定为0 或1时按照某某连接 排列 然而 当连接轴值较大时 如3 4 5 笔者
  • vue3 的组件通信以及ref的使用&v-model

    一 组件通信 1 props gt 父向子传值 props主要用于父组件向子组件通信 在父组件中通过用 msg msg 绑定需要传给子组件的属性值 然后再在子组件中用 props 接收该属性值 方法一 普通方式 父组件 传值
  • linux 编译protobuf(MacOS或者UOS),Protobuf为3.5.1

    先下载好Protobuf 自己网上找 或者用我下面上传的包 我们采用编译成静态库的模式 就不生成 so文件了 解压后进行编译前的准备工作 cd protobuf 3 5 1 进入目录 autogen sh 执行下生成configure文件
  • 储存型XSS和DOM型XSS的解析

    储存型XSS和DOM型XSS的解析 存储型XSS漏洞 存储型的XSS漏洞和反射型形成的原因是一样的 也在输入输出时造成的问题 不同的是存储型的XSS下可以将攻击者的脚本注入后台存储起来 结构更加持久的危害 因此存储型XSS也称 永久型 XS
  • 力扣 91. 解码方法

    一开始的想法是 每个字母的编码要么是一位数字 要么是两位数字 所以从尾部开始看是否能取出一位数字或者两位数字 再讨论取出数字后剩下的字符串有几种解码方式 其实就是动态规划的思想 由于今天第一次做动规的题 第一想法是用递归求解 结果超时了 然
  • 实测aiCache加速效能测试报告

    服务器 AMD双核DualCore QL 62 系统搭载743M的 RAM 运行64位Ubuntu 9 1 软件 Apache2 配置 未修改的缺省配置 Apache2配置侦听82端口 aiCache采用HTTP80端口 测试细节 Apac
  • window.print()打印时根据页面高度设置居中显示、设置打印布局(纵向、横向)

    1 页面定义打印div按钮 div style background color EDEDED span class fa fa print hidden print title 打印 style font size 24px span d
  • c++中成员初始化(普通成员变量和静态成员变量)

    1 c 中普通成员变量的初始化 1 对于普通变量直接在头文件声明的时候就进行初始化 c 11的标准下是可以直接这样使用的 2 就是通过初始化列表进行初始化 3 在构造函数中进行初始化 以下通过一段代码的初始化工作 可以看出来 成员变量初始化
  • 深入理解 Go 调度模型 GPM

    Author mogd 2022 04 28 Update mogd 2022 05 05 Adage Be content with what you have rejoice in the way things are When you
  • C++面向对象(黑马程序员)

    内存分区模型 include
  • 常见的几种缓存软件以及使用缓存需要考虑的问题

    目录 几种缓存 缓存使用中存在的问题 缓存不足 缓存击穿问题 热点数据
  • UE4C++ Http下载文件

    成果 功能 点击一个按钮可以远程下载文件 并看得到进度条 先上代码 h文件 Fill out your copyright notice in the Description page of Project Settings pragma
  • webpack的loader与plugin原理

    文章目录 loader原理 loader执行顺序 loader使用方式 loader的实现 参数 同步loader 异步loader Raw loader Pitching loader loader API 手写clean log loa
  • FIR带通滤波器设计与验证

    前言 复习大学课程中数字信号处理的内容 尽管学了很多理论知识 但与实际的联系还是比较少 也就是说学到的知识不能很好地运用于实践 所以我打算对理论知识进行验证 从MATLAB和FPGA入手 完成滤波器的设计 体会模拟信号数字处理的过程 同时也
  • 【分类】分类性能评价

    评价指标 1 准确率 召回率 精确率 F 度量 ROC 属于各类的样本的并不是均一分布 甚至其出现概率相差很多个数量级 这种分类问题称为不平衡类问题 在不平衡类问题中 准确率并没有多大意义 我们需要一些别的指标 通常在不平衡类问题中 我们使
  • TVP思享

    导语 当产品的用户量不断翻番时 需求会倒逼着你优化HTTP协议 那么 要想极限优化HTTP性能 应该从哪些维度出发呢 本文将由TVP陶辉老师 为大家分享四个全新维度 TVP思享 专栏 凝结大咖思考 汇聚专家分享 收获全新思想 欢迎长期关注
  • 从零开始搞监控系统(3)——性能监控

    前端性能监控是个老话题了 各个团队都会对其有所关注 因为关注性能是工程师的本分 页面性能对用户体验而言十分关键 每次重构或优化 仅靠手中的几个设备或模拟的测试 缺少说服力 需要有大量的真实数据来做验证 这次不同 公司急切的需要一套性能监控系