【性能篇】29 # 怎么给Canvas绘制加速?

news2025/2/4 10:37:36

说明

【跟月影学可视化】学习笔记。

方法一:优化 Canvas 指令

例子:实现一些位置随机的多边形,并且不断刷新这些图形的形状和位置

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>优化 Canvas 指令</title>
        <style>
            canvas {
                border: 1px dashed #fa8072;
            }
        </style>
    </head>
    <body>
        <canvas width="500" height="500"></canvas>
        <script>
            const canvas = document.querySelector("canvas");
            const ctx = canvas.getContext("2d");

            // 创建正多边形,返回顶点
            function regularShape(x, y, r, edges = 3) {
                const points = [];
                const delta = (2 * Math.PI) / edges;
                for (let i = 0; i < edges; i++) {
                    const theta = i * delta;
                    points.push([
                        x + r * Math.sin(theta),
                        y + r * Math.cos(theta),
                    ]);
                }
                return points;
            }

            // 根据顶点绘制图形
            function drawShape(context, points) {
                context.fillStyle = "red";
                context.strokeStyle = "black";
                context.lineWidth = 2;
                context.beginPath();
                context.moveTo(...points[0]);
                for (let i = 1; i < points.length; i++) {
                    context.lineTo(...points[i]);
                }
                context.closePath();
                context.stroke();
                context.fill();
            }

            // 多边形类型,包括正三角形、正四边形、正五边形、正六边形和正100边形、正500边形
            const shapeTypes = [3, 4, 5, 6, 100, 500];
            const COUNT = 1000;

            // 执行绘制
            function draw() {
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                for (let i = 0; i < COUNT; i++) {
                    const type =
                        shapeTypes[
                            Math.floor(Math.random() * shapeTypes.length)
                        ];
                    const points = regularShape(
                        Math.random() * canvas.width,
                        Math.random() * canvas.height,
                        10,
                        type
                    );
                    drawShape(ctx, points);
                }
                requestAnimationFrame(draw);
            }

            draw();
        </script>
    </body>
</html>

我们f12查看帧率,效果如下:Google Chrome浏览器怎么开启查看帧率功能?

在这里插入图片描述

对于一个 500 边形来说,它的顶点数量非常多,所以 Canvas 需要执行的绘图指令也会非常多,那绘制很多个 500 边形自然会造成性能问题。

下面减少绘制 500 边形的绘图指令的数量:用 -1 代替正 500 边形,如果type小于0表名多边形是正500边形,用 arc 指令来画圆

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>优化 Canvas 指令</title>
        <style>
            canvas {
                border: 1px dashed #fa8072;
            }
        </style>
    </head>
    <body>
        <canvas width="500" height="500"></canvas>
        <script>
            const canvas = document.querySelector("canvas");
            const ctx = canvas.getContext("2d");

            // 创建正多边形,返回顶点
            function regularShape(x, y, r, edges = 3) {
                const points = [];
                const delta = (2 * Math.PI) / edges;
                for (let i = 0; i < edges; i++) {
                    const theta = i * delta;
                    points.push([
                        x + r * Math.sin(theta),
                        y + r * Math.cos(theta),
                    ]);
                }
                return points;
            }

            // 根据顶点绘制图形
            function drawShape(context, points) {
                context.fillStyle = "red";
                context.strokeStyle = "black";
                context.lineWidth = 2;
                context.beginPath();
                context.moveTo(...points[0]);
                for (let i = 1; i < points.length; i++) {
                    context.lineTo(...points[i]);
                }
                context.closePath();
                context.stroke();
                context.fill();
            }

            // 多边形类型,包括正三角形、正四边形、正五边形、正六边形和正100边形以及正500边形
            // 用 -1 代替正 500 边形
            const shapeTypes = [3, 4, 5, 6, 100, -1];
            const COUNT = 1000;
            const TAU = Math.PI * 2;

            // 执行绘制
            function draw() {
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                for (let i = 0; i < COUNT; i++) {
                    const type =
                        shapeTypes[
                            Math.floor(Math.random() * shapeTypes.length)
                        ];
                    const x = Math.random() * canvas.width;
                    const y = Math.random() * canvas.height;
                    // 如果type小于0表名多边形是正500边形
                    if(type > 0) {
                        // 画正多边形
                        const points = regularShape(x, y, 10, type);
                        drawShape(ctx, points);
                    } else {
                        // 画圆
                        ctx.beginPath();
                        // 绘制正多边形,否则用 arc 指令来画圆
                        ctx.arc(x, y, 10, 0, TAU);
                        ctx.stroke();
                        ctx.fill();
                    }
                }
                requestAnimationFrame(draw);
            }

            draw();
        </script>
    </body>
</html>

优化完之后的效果:

在这里插入图片描述

方法二:使用缓存

具体做法就是将图形缓存下来,保存到离屏的 Canvas(offscreen Canvas)中,然后在绘制的时候作为图像来渲染,那就可以将绘制顶点的绘图指令变成直接通过 drawImage 指令来绘制图像,而且也不需要 fill() 方法来填充图形。

https://developer.mozilla.org/zh-CN/docs/Web/API/OffscreenCanvas

OffscreenCanvas 提供了一个可以脱离屏幕渲染的 canvas 对象。它在窗口环境和web worker环境均有效。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>使用缓存</title>
        <style>
            canvas {
                border: 1px dashed #fa8072;
            }
        </style>
    </head>
    <body>
        <canvas width="500" height="500"></canvas>
        <script>
            const canvas = document.querySelector("canvas");
            const ctx = canvas.getContext("2d");

            // 创建缓存的函数
            function createCache() {
                const ret = [];
                for (let i = 0; i < shapeTypes.length; i++) {
                    // 创建离屏Canvas缓存图形
                    const cacheCanvas = new OffscreenCanvas(20, 20);
                    // 将图形绘制到离屏Canvas对象上
                    const type = shapeTypes[i];
                    const context = cacheCanvas.getContext("2d");
                    context.fillStyle = "red";
                    context.strokeStyle = "black";
                    if (type > 0) {
                        const points = regularShape(10, 10, 10, type);
                        drawShape(context, points);
                    } else {
                        context.beginPath();
                        context.arc(10, 10, 10, 0, TAU);
                        context.stroke();
                        context.fill();
                    }
                    ret.push(cacheCanvas);
                }
                // 将离屏Canvas数组(缓存对象)返回
                return ret;
            }

            // 创建正多边形,返回顶点
            function regularShape(x, y, r, edges = 3) {
                const points = [];
                const delta = (2 * Math.PI) / edges;
                for (let i = 0; i < edges; i++) {
                    const theta = i * delta;
                    points.push([
                        x + r * Math.sin(theta),
                        y + r * Math.cos(theta),
                    ]);
                }
                return points;
            }

            // 根据顶点绘制图形
            function drawShape(context, points) {
                context.fillStyle = "red";
                context.strokeStyle = "black";
                context.lineWidth = 2;
                context.beginPath();
                context.moveTo(...points[0]);
                for (let i = 1; i < points.length; i++) {
                    context.lineTo(...points[i]);
                }
                context.closePath();
                context.stroke();
                context.fill();
            }

            // 多边形类型,包括正三角形、正四边形、正五边形、正六边形和正100边形以及正500边形
            const shapeTypes = [3, 4, 5, 6, 100, 500];
            const COUNT = 1000;
            const TAU = Math.PI * 2;

            // 一次性创建缓存,直接通过缓存来绘图
            const shapes = createCache();

            function draw() {
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                for (let i = 0; i < COUNT; i++) {
                    const shape = shapes[Math.floor(Math.random() * shapeTypes.length)];
                    const x = Math.random() * canvas.width;
                    const y = Math.random() * canvas.height;
                    ctx.drawImage(shape, x, y);
                }
                requestAnimationFrame(draw);
            }

            draw();
        </script>
    </body>
</html>

开启缓存效果:

在这里插入图片描述

缓存的局限性

  1. 如果需要创建大量的离屏 Canvas 对象,就会对内存消耗就非常大,有可能反而降低了性能。
  2. 缓存适用于图形状态本身不变的图形元素,如果是经常发生状态改变的图形元素,起不到减少绘图指令的作用。
  3. 不使用缓存直接绘制的是矢量图,而通过缓存 drawImage 绘制出的则是位图,所以缓存绘制的图形,在清晰度上可能不是很好。

方法三:分层渲染

简单点说就是用两个 Canvas 叠在一起,将不变的元素绘制在一个 Canvas 中,变化的元素绘制在另一个 Canvas 中。

满足两个条件

  • 一是有大量静态的图形元素不需要重新绘制
  • 二是动态和静态图形元素绘制顺序是固定的,先绘制完静态元素再绘制动态元素

在这里插入图片描述

上面就是两个canvas,一个动的,一个静态的,我们把它们叠在一起

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>分层渲染</title>
        <style>
            .box {
                position: relative;
            }
            canvas {
                border: 1px dashed #fa8072;
            }
            #bg {
                background-color: #000;
            }
            #fg {
                position: absolute;
                top: 0;
                left: 0;
            }
        </style>
    </head>
    <body>
        <div class="box">
            <canvas width="500" height="500" id="bg"></canvas>
            <canvas width="500" height="500" id="fg"></canvas>
        </div>
        <script>
            // 绘制随机三角形
            function drawRandomTriangle(path, context) {
                const { width, height } = context.canvas;
                context.save();
                context.translate(
                    Math.random() * width,
                    Math.random() * height
                );
                context.fill(path);
                context.restore();
            }

            // 绘制背景
            function drawBackground(context, count = 2000) {
                context.fillStyle = "#ed7";
                const d = "M0,0L0,10L8.66, 5z";
                const p = new Path2D(d);
                for (let i = 0; i < count; i++) {
                    drawRandomTriangle(p, context);
                }
            }

            // 加载图片
            function loadImage(src) {
                const img = new Image();
                img.crossOrigin = "anonymous";
                return new Promise((resolve) => {
                    img.onload = resolve(img);
                    img.src = src;
                });
            }

            // 绘制前置背景
            async function drawForeground(context) {
                const img = await loadImage('./assets/img/plane.png');
                const { width, height } = context.canvas;
                function update(t) {
                    context.clearRect(0, 0, width, height);
                    context.save();
                    context.translate(0, 0.5 * height);
                    const p = (t % 3000) / 3000;
                    const x = width * p;
                    const y = 0.1 * height * Math.sin(3 * Math.PI * p);
                    context.drawImage(img, x, y);
                    context.restore();
                    requestAnimationFrame(update);
                }
                update(0);
            }

            const bgcanvas = document.querySelector("#bg");
            const fgcanvas = document.querySelector("#fg");
            drawBackground(bgcanvas.getContext("2d"));
            drawForeground(fgcanvas.getContext("2d"));
        </script>
    </body>
</html>

方法四:局部重绘

如果元素都有可能运动,或者动态元素和静态元素的绘制顺序是交错的,可以使用局部重绘来处理,局部重绘就是不需要清空 Canvas 的全局区域,而是根据运动的元素的范围来清空部分区域。

canvas 提供 clip() ,能确定绘制的的裁剪区域,区域之外的图形不能绘制 CanvasRenderingContext2D.clip()

另外可以使用动态计算要重绘区域的技术,它也被称为脏区检测。它的基本原理是根据动态元素的包围盒,动态算出需要重绘的范围。

包围盒:指能包含多边形所有顶点,并且与坐标轴平行的最小矩形。

在这里插入图片描述

有兴趣的可以看看这篇:AntV Canvas 局部渲染总结

方法五:优化滤镜

用缓存优化版本的代码加上滤镜

在这里插入图片描述
可以看到直接干到 1.8 fps 了,说明滤镜对渲染性能的开销还是很大的
在这里插入图片描述

我们可以对 Canvas 应用一个全局的 blur 滤镜,把绘制的所有元素都变得模糊,没必要对每个元素应用滤镜,而是可以采用类似后期处理通道的做法,先将图形以不使用滤镜的方式绘制到一个离屏的 Canvas 上,然后直接将这个离屏 Canvas 以图片方式绘制到要显示的画布上,这样就能把大量滤镜绘制的过程缩减为对一张图片使用一次滤镜,下面调整一下代码:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>优化滤镜</title>
        <style>
            canvas {
                border: 1px dashed #fa8072;
            }
        </style>
    </head>
    <body>
        <canvas width="500" height="500"></canvas>
        <script>
            const canvas = document.querySelector("canvas");
            const ctx = canvas.getContext("2d");

            // 创建缓存的函数
            function createCache() {
                const ret = [];
                for (let i = 0; i < shapeTypes.length; i++) {
                    // 创建离屏Canvas缓存图形
                    const cacheCanvas = new OffscreenCanvas(20, 20);
                    // 将图形绘制到离屏Canvas对象上
                    const type = shapeTypes[i];
                    const context = cacheCanvas.getContext("2d");
                    context.fillStyle = "red";
                    context.strokeStyle = "black";
                    if (type > 0) {
                        const points = regularShape(10, 10, 10, type);
                        drawShape(context, points);
                    } else {
                        context.beginPath();
                        context.arc(10, 10, 10, 0, TAU);
                        context.stroke();
                        context.fill();
                    }
                    ret.push(cacheCanvas);
                }
                // 将离屏Canvas数组(缓存对象)返回
                return ret;
            }

            // 创建正多边形,返回顶点
            function regularShape(x, y, r, edges = 3) {
                const points = [];
                const delta = (2 * Math.PI) / edges;
                for (let i = 0; i < edges; i++) {
                    const theta = i * delta;
                    points.push([
                        x + r * Math.sin(theta),
                        y + r * Math.cos(theta),
                    ]);
                }
                return points;
            }

            // 根据顶点绘制图形
            function drawShape(context, points) {
                context.fillStyle = "red";
                context.strokeStyle = "black";
                context.lineWidth = 2;
                context.beginPath();
                context.moveTo(...points[0]);
                for (let i = 1; i < points.length; i++) {
                    context.lineTo(...points[i]);
                }
                context.closePath();
                context.stroke();
                context.fill();
            }

            // 多边形类型,包括正三角形、正四边形、正五边形、正六边形和正100边形以及正500边形
            const shapeTypes = [3, 4, 5, 6, 100, 500];
            const COUNT = 1000;
            const TAU = Math.PI * 2;

            // 一次性创建缓存,直接通过缓存来绘图
            const shapes = createCache();
            ctx.filter = "blur(5px)";

            // 创建离屏的 Canvas
            const ofc = new OffscreenCanvas(canvas.width, canvas.height);
            const octx = ofc.getContext("2d");
            function draw() {
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                octx.clearRect(0, 0, canvas.width, canvas.height);
                // 将图形不应用滤镜,绘制到离屏Canvas上
                for (let i = 0; i < COUNT; i++) {
                    const shape =
                        shapes[Math.floor(Math.random() * shapeTypes.length)];
                    const x = Math.random() * canvas.width;
                    const y = Math.random() * canvas.height;
                    octx.drawImage(shape, x, y);
                }
                // 再将离屏Canvas图像绘制到画布上,这一次绘制采用了滤镜
                ctx.drawImage(ofc, 0, 0);
                requestAnimationFrame(draw);
            }

            draw();
        </script>
    </body>
</html>

可以看到效果立竿见影:

在这里插入图片描述

方法六:多线程渲染

多线程渲染是用来优化非渲染的计算和交互方面导致的性能问题。比如渲染过程消耗了大量的时间,它可能会阻塞其他的操作,比如对事件的响应。这个时候可以利用浏览器支持 Canvas 可以在 WebWorker 中以单独的线程来渲染,这样就可以避免对主线程的阻塞,也不会影响用户交互行为。

具体的过程:

  1. 在浏览器主线程中创建 Worker
  2. 然后将 Canvas 对象通过 transferControlToOffscreen 转成离屏 Canvas 对象发送给 Worker 线程去处理

方法 HTMLCanvasElement.transferControlToOffscreen() 将控制转移到一个在主线程或者 web worker 的 OffscreenCanvas 对象上。

postMessage用法可以参考:https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage

我们新建一个 worker.js 文件,在里面写进之前优化好的代码,并且添加监听

// 创建正多边形,返回顶点
function regularShape(x, y, r, edges = 3) {
    const points = [];
    const delta = (2 * Math.PI) / edges;
    for (let i = 0; i < edges; i++) {
        const theta = i * delta;
        points.push([x + r * Math.sin(theta), y + r * Math.cos(theta)]);
    }
    return points;
}

// 根据顶点绘制图形
function drawShape(context, points) {
    context.lineWidth = 2;
    context.beginPath();
    context.moveTo(...points[0]);
    for (let i = 1; i < points.length; i++) {
        context.lineTo(...points[i]);
    }
    context.closePath();
    context.stroke();
    context.fill();
}

// 多边形类型,包括正三角形、正四边形、正五边形、正六边形和正100边形以及正500边形
// 用 -1 代替正 500 边形
const shapeTypes = [3, 4, 5, 6, 100, -1];
const COUNT = 1000;
const TAU = Math.PI * 2;

// 创建缓存的函数
function createCache() {
    const ret = [];
    for (let i = 0; i < shapeTypes.length; i++) {
        const cacheCanvas = new OffscreenCanvas(20, 20);
        const type = shapeTypes[i];
        const context = cacheCanvas.getContext("2d");
        context.fillStyle = "red";
        context.strokeStyle = "black";
        if (type > 0) {
            const points = regularShape(10, 10, 10, type);
            drawShape(context, points);
        } else {
            context.beginPath();
            context.arc(10, 10, 10, 0, TAU);
            context.stroke();
            context.fill();
        }
        ret.push(cacheCanvas);
    }
    return ret;
}

// 执行绘制
function draw(ctx, shapes) {
    const canvas = ctx.canvas;
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    for (let i = 0; i < COUNT; i++) {
        const shape = shapes[Math.floor(Math.random() * shapeTypes.length)];
        const x = Math.random() * canvas.width;
        const y = Math.random() * canvas.height;
        ctx.drawImage(shape, x, y);
    }
    requestAnimationFrame(draw.bind(null, ctx, shapes));
}

// 监听message
console.log('self------>', self)
self.addEventListener("message", (evt) => {
    console.log('message--->', evt)
    if (evt.data.type === "init") {
        const canvas = evt.data.canvas;
        if (canvas) {
            const ctx = canvas.getContext("2d");
            const shapes = createCache();
            draw(ctx, shapes);
        }
    }
});

在 html 文件里面引入该 js

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>多线程渲染</title>
        <style>
            canvas {
                border: 1px dashed #fa8072;
            }
        </style>
    </head>
    <body>
        <canvas width="500" height="500"></canvas>
        <script>
            const canvas = document.querySelector('canvas');

            const worker = new Worker('./assets/js/29/worker.js');
            const ofc = canvas.transferControlToOffscreen();
            worker.postMessage({
                canvas: ofc,
                type: 'init',
            }, [ofc]);
        </script>
    </body>
</html>

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/103473.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

openGauss的WDR报告解读

文章目录1.执行以下SQL命令,查询已经生成的快照信息。2.生成WDR报告。3.手工创建快照信息4.WDR涉及的数据表5.WDR报告解读在Oralce数据库中&#xff0c;遇到性能问题&#xff0c;我们通常会查看有无对应时间段的快照&#xff0c;生成awr报告并进一步分析&#xff08;AWR是Autom…

海量数据小内存!只出现两次的数以及中位数怎么找

文章目录题目一题目二实际上类似的题目类似的解法在之前已经有介绍过海量数据小内存&#xff01;如何找到高频数 海量数据小内存&#xff01;从未出现过的数在哪里 题目一 如何在 40 亿个无符号整数中找到出现次数只有两次的那些数&#xff0c;在只提供 1 G 内存的条件下 解…

Map集合概述、API 遍历方式(键值对集合)

注意&#xff1a; Map集合和Collection集合是两个不同类型的集合 Map集合体系特点&#xff1a; 常用API&#xff1a; 根据键找出值&#xff1a; map.get(key); 取所有键的集合和取所有值得集合&#xff1a; 因为key是无序不重复无索引&#xff0c;所以放入set集合&#xff…

锂电池电压和电量的关系

锂电池电压和电量之间,有一定的对应关系,通过对开路电压的测量,可以大致得出电池的剩余电量。不过用电压测量电量的方式有一定的不稳定性,例如放电电流、环境温度、循环、放电平台、电极材料等,都会给最后结果的准确与否带来影响。 电压和电量的对应关系是: 100%----4.…

【算法】常用查找算法(顺序查找、二分查找、插值查找、斐波那契查找)

目录查找算法1.线性(顺序)查找(1)思路(2)代码实现(java)2.二分(折半)查找(1)思路(2)代码实现(java)3.插值查找(1)思路(2)代码实现(java)4.斐波那契(黄金分割法)查找(1)思路(2)代码实现(java)查找算法 1.线性(顺序)查找 (1)思路 判断序列中是否包含某个元素&#xff0c;找到提…

Vue3引入Lottie动画以及遇到的坑

之所以写这个问题是因为原本我认为非常小的一件事却困扰了我一整天&#xff0c;所以我打算写一个博客记录一番。 国外动画网址&#xff1a;Lottie 将来用到的lottie组件库网址&#xff1a; Vue3-lottie 我目前用的第二个&#xff1a; Vue3-lottiejs 1. 我在引入Lottie的时…

【Python机器学习】决策树与随机森林的讲解及决策树在决策决策问题中实战(图文解释 附源码)

需要源码请点赞关注收藏后评论区留言私信~~~ 在生活中人们经常应用决策树的思想来做决定 分类的建模过程与上面做决定的过程相反&#xff0c;事先不知道人们的决策思路&#xff0c;需要通过人们已经做出的大量决定来“揣摩”出其决策思路&#xff0c;也就是通过大量数据来归纳道…

嵌入式分享合集124

一、19个常用的5V转3.3V技巧 01 使用LDO稳压器 标准三端线性稳压器的压差通常是 2.0-3.0V。要把 5V 可靠地转换为 3.3V&#xff0c;就不能使用它们。压差为几百个毫伏的低压降 &#xff08;Low Dropout&#xff0c; LDO&#xff09;稳压器&#xff0c;是此类应用的理想选择。图…

常见的CSS布局方法

常见的CSS布局方法 「1. 单栏布局」 常见的单列布局有两种: header,content 和 footer 等宽的单列布局header 与 footer 等宽,content 略窄的单列布局header,content 和 footer 等宽的单列布局 ​ 先通过对 header,content,footer 统一设置 width:1000px;或者 max-width:1…

DHCP报文

一. 介绍 DHCP&#xff08;Dynamic Host Configuration Protocol&#xff0c;动态主机配置协议&#xff09;是一个局域网的网络协议&#xff0c;使用UDP协议工作&#xff0c;统一使用两个IANA分配的端口&#xff1a;67&#xff08;服务器端&#xff09;&#xff0c;68&#xff…

Django学习Day5

由于前两天核酸阳的&#xff0c;一直发烧&#xff0c;故没有学习&#xff0c;csdn也没有进行更新。今天身体基本恢复&#xff0c;继续Django的学习旅程。也希望各位读者重视个人的身体健康&#xff0c;做好自己健康的第一负责人。 1.关于针对模型类的数据库修改方法补充 在mo…

二苯基环辛炔-氨基;DBCO-NH2科研实验用试剂DBCO-Amine;CAS:1255942-06-3

英文名称&#xff1a;DBCO-Amine DBCO-NH2 中文名称&#xff1a;二苯基环辛炔-氨基 CAS&#xff1a;1255942-06-3 分子式&#xff1a;C18H16N2 分子量&#xff1a;276.3 外观&#xff1a;固体粉末 溶剂&#xff1a;溶于 DMSO, DMF, DCM, THF, Chloroform 储存条件&…

什么是容器安全性,您如何提升自己的安全性?

容器无疑已成为部署应用程序的流行方式。这很棒&#xff0c;因为与部署到虚拟机相比&#xff0c;它们具有大量优势。其中一些优点包括便携、不可变和轻量级。您可以控制运行服务的容器内部的内容&#xff0c;这可以产生清晰、可审计的跟踪。 对于安全专业人员来说&#xff0c;…

模型复杂度与硬件性能的衡量

1. 模型复杂度的衡量 参数数量&#xff08;Params&#xff09;&#xff1a;指模型含有多少参数&#xff0c;直接决定模型的大小&#xff0c;也影响推断时对内存的占用量 单位通常为 M&#xff0c;通常参数用 float32 表示&#xff0c;所以模型大小是参数数量的 4 倍左右参数数…

数据结构C语言版 —— 树和二叉树的概念

树和二叉树 一、树 1. 树的概念 树(Tree)是n(n>0)n(n>0)n(n>0)个节点的有限集&#xff0c;在任意一颗非空树中&#xff1a; (1) 有且仅有一个特定的称为根(Root)的节点&#xff0c;根节点是没有前驱节点的。 &#xff08;2&#xff09;当 n>1n > 1n>1时…

_11LeetCode代码随想录算法训练营第十一天-C++队列的应用

_11LeetCode代码随想录算法训练营第十一天-C队列的应用 239.滑动窗口最大值347.前K个高频元素 239.滑动窗口最大值 整体思路 要实现一个单调递减队列&#xff1a; 对于滑动窗口的滑动&#xff0c;移除前面的元素&#xff0c;加入后面的元素。当移除前面的元素时&#xff0…

监控物联网卡该如何选择,你都踩过哪些坑?

不知道大家有没有发现在自己的身边不知不觉多了很多新玩意&#xff0c;例如智能自动售货机、共享单车、智能监控设备等&#xff0c;它们让大家的生活变得越来越方便&#xff0c;那么大家知道它们为什么能起到这么大的作用吗&#xff0c;其实得得益于一个叫做物联网卡的东西。前…

通过kubeode安装k8s

文章目录通过kubeode安装k8s1、准备vmdk文件2、创建虚拟机3、进入虚拟机4、配置yum源5、清理6、 增加node服务器7、修改Ip8、下载下载通道01 走普通家庭宽带下载点下载通道02 走群友无私赞助电信机房专线服务器--高速稳定下载----强烈推荐下载并解压9、一键安装通过kubeode安装…

字符串函数剖析(1)

带你玩转字符串 1.strlen函数不一样的细节 1.1模拟实现strlen函数 2.strcpy函数的巧妙 2.2strcpy的模拟实现 3.strcmp函数的巧妙 3.2strcmp的模拟实现 详解strlen的细节 首先了解strlen 函数的参数 size_t strlen ( const char * str );size_t 是什么东西呢&#xff1f…

链表-------数据结构

链表(重点): 链表是物理存储结构上面非连续的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的引用链接次序实现的 1)在顺序表中&#xff0c;我们不光引入了一段连续的内存&#xff0c;还引入了一块连续的内存空间&#xff0c;叫做usedsize&#xff0c;来表示对应数组中…