p5.js中函数首次调用耗时远高于后续调用的原因探究
瓦片棋盘绘制性能测试:首次调用耗时远高于后续的原因分析
我在测试瓦片棋盘的绘制性能,有两个可选方案:逐块绘制瓦片,或者先把瓦片填充到图像中再绘制静态图。绘制静态图的速度比逐块绘制快很多,但我发现函数首次调用的耗时明显高于后续调用,是后续的4-10倍,想知道这个现象的原因是什么?
测试代码
let img; let NROWS = 55; let NCOLS = 29; const TILE_WIDTH_HALF = 32; const TILE_HEIGHT_HALF = 16; const HALF_WIDTH = 950; const HALF_HEIGHT = 450; let start, end; let bg; function preload() { img = loadImage("tile.png"); } // Populates the background graphics function populateBackground() { bg = createGraphics(HALF_WIDTH*2, HALF_HEIGHT*2); for (let row=0; row<NROWS; row++) { for (let col=0; col<NCOLS; col++) { x = col*TILE_WIDTH_HALF*2 + (TILE_WIDTH_HALF * (row%2)+1) y = row*TILE_HEIGHT_HALF; bg.image(img, x, y); } } } // draws tile by tile into the canvas function drawTileByTile() { background(100); start = millis(); for (let row=0; row<NROWS; row++) { for (let col=0; col<NCOLS; col++) { x = col*TILE_WIDTH_HALF*2 + (TILE_WIDTH_HALF * (row%2)+1) - HALF_WIDTH; y = row*TILE_HEIGHT_HALF - HALF_HEIGHT; image(img, x, y); } } end = millis(); return (end-start); } // draws the prepopulated graphics as a single image function drawAsImage() { background(100); start = millis(); image(bg,-HALF_WIDTH,-HALF_HEIGHT); end = millis(); return (end-start); } function setup() { createCanvas(HALF_WIDTH*2, HALF_HEIGHT*2, WEBGL); populateBackground(); // Draw tile by tile 10 times let elapsed_ms = []; for (let n=0; n<10; n++) { elapsed_ms.push(drawTileByTile().toFixed(3)); } console.log(elapsed_ms); // Draw as image 10 times elapsed_ms = []; for (let n=0; n<10; n++) { elapsed_ms.push(drawAsImage().toFixed(3)); } console.log(elapsed_ms); noLoop(); } function draw() { }
测试耗时数据
sketch.js:61 (10) ['114.400', '35.400', '37.000', '31.800', '54.200', '51.900', '33.000', '34.400', '30.200', '31.200'] sketch.js:68 (10) ['1.000', '0.100', '0.100', '0.000', '0.100', '0.000', '0.000', '0.000', '0.000', '0.000']
原因分析
这个现象核心是GPU的资源初始化与缓存机制在起作用,具体可以拆解成几点:
- 首次调用的资源初始化开销:第一次调用
image()绘制时,GPU需要完成一系列一次性的准备工作——比如把图像数据从CPU内存上传到GPU显存、创建纹理对象、配置渲染管线的参数(像着色器、纹理采样方式)。这些操作都需要消耗时间,后续调用时已经完成了初始化,就不用再重复执行。 - GPU缓存复用:后续绘制时,GPU会把已经加载好的纹理、渲染状态等数据缓存起来,直接复用这些缓存资源就能快速完成绘制,省去了重新处理资源的流程。
- WebGL上下文预热:因为你用了
WEBGL模式创建画布,第一次执行绘制操作时,WebGL上下文本身也需要启动和预热,比如初始化默认着色器、设置视口、配置渲染环境等,这些都会拉高首次调用的耗时,之后上下文处于就绪状态,操作效率就会大幅提升。
另外看你测试里的drawAsImage,首次耗时1ms,后续几乎为0,也完全符合这个逻辑:第一次把预生成的bg纹理上传到GPU,之后直接复用这个纹理绘制,几乎没有额外开销。
内容的提问来源于stack exchange,提问作者user20396240
相关产品推荐
相关产品推荐

