スピードテスト(runCommand VS Script)

スポンサーリンク

概要

runCommand() と Script の処理時間を比較します。
この記事は影月氏の hasItemスピードテスト(コマンド VS ScriptAPI) のインスパイアです。

おそらく他処理の検証も追記します。

バージョン:@minecraft/server 1.13.0

検証

検証内容 : スコア検知

プレイヤーまたは仮想スコアに、特定のスコア値があるかどうか返すプログラム(10000回)

プレイヤースコアが1かどうか
スコア無し  command : 306 ms / script 108
スコア不一致 command : 264 ms / script   75
スコア一致  command : 271 ms / script   74

仮想スコア(test)が1かどうか
スコア無し  command : 321 ms / script 106
スコア不一致 command : 296 ms / script   77
スコア一致  command : 267 ms / script   76

Script が優勢

// 特定のスコア値を持っているかどうかを返す関数
function hasScoreScript(target, objectiveId, score) {
    try {
        const objective = world.scoreboard.getObjective(objectiveId);
        return objective?.getScore(target) === score;
    } catch {
        return false;
    }
}
function hasScoreCommand(target, objectiveId, score) {
    let result;
    if (typeof target === "string") {
        result = world.getDimension("overworld").runCommand(`execute if score ${target} ${objectiveId} matches ${score}`);
    } else {
        result = target.runCommand(`execute if score @s ${objectiveId} matches ${score}`);
    }
    return result.successCount === 1;
}

検証内容 : タグの追加・削除

特定の座標とディメンションにおいて、指定したブロックを設置するプログラム(10000回)

1回目 command : 480 ms / script  66
2回目 command : 495 ms / script  86
3回目 command : 469 ms / script  63

Script が優勢

// プレイヤーにタグのつけ外しを行う関数
function addRemoveTagCommand(player, tag) {
    player.runCommand(`tag @s add ${tag}`);
    player.runCommand(`tag @s remove ${tag}`);
}
function addRemoveTagScript(player, tag) {
    player.addTag(tag);
    player.removeTag(tag);
}

検証内容 : ブロックの設置

特定の座標とディメンションに、指定したブロックを設置するプログラム(10000回)

<Dimension>.getBlock().setType() | .setPermutation() の場合
ブロックstates無

1回目 command : 327 ms / script(setPerm) 127 / script(setType) 116
2回目 command : 309 ms / script(setPerm) 123 / script(setType) 113
3回目 command : 304 ms / script(setPerm) 122 / script(setType) 113

ブロックstates有
1回目 command : 361 ms / script(setPerm) 201
2回目 command : 359 ms / script(setPerm) 203
3回目 command : 342 ms / script(setPerm) 230

<Dimension>.setType() | .setPermutation() の場合

ブロックstates無
1回目 script(setPerm) 85 / script(setType) 74
2回目 script(setPerm) 85 / script(setType) 74
3回目 script(setPerm) 90 / script(setType) 79

ブロックstates有
1回目 script(setPerm) 187
2回目 script(setPerm) 182
3回目 script(setPerm) 195

Script が優勢
ブロックStatesが無い場合は setType() のほうが優勢
さらに、getBlock()を挟まず、<dimension>.setType() | .setPermutation() のほうが優勢

// 座標に特定のブロックを設置する関数
// コマンド
function setBlockCommand(dimensionId, location, blockId, states = "[]") {
    const { x, y, z } = location;
    world.getDimension(dimensionId).runCommand(`setblock ${x} ${y} ${z} ${blockId} ${states}`);
}
// Script 1 getBlock() を行う場合
function setPermScript(dimensionId, location, blockId, states = undefined) {
    const blockPerm = BlockPermutation.resolve(blockId, states);
    world.getDimension(dimensionId).getBlock(location).setPermutation(blockPerm);
}
function setTypeScript(dimensionId, location, blockId) {
    world.getDimension(dimensionId).getBlock(location).setType(blockId);
}
// Script 2 getBlock() を行わない場合
function setPermScript2(dimensionId, location, blockId, states = undefined) {
    const blockPerm = BlockPermutation.resolve(blockId, states);
    world.getDimension(dimensionId).setBlockPermutation(location, blockPerm);
}
function setTypeScript2(dimensionId, location, blockId) {
    world.getDimension(dimensionId).setBlockType(location, blockId);
}

検証内容 : ブロックの範囲設置

一定範囲(32×32×32)に指定したブロックを設置するプログラム(1回)

範囲内がすべて空気の場合
1回目 command : 40 ms / script(setPerm) 170 / script(fillBlocks) 66
2回目 command : 41 ms / script(setPerm) 169 / script(fillBlocks) 42
3回目 command : 43 ms / script(setPerm) 171 / script(fillBlocks) 64

範囲内がすべて設置済みの場合
command : 5 ms / script(setPerm) 130 / script(fillBlocks)  5
※ fillBlocks() はベータ版のみ有効

Command が優勢
fillBlocks() も同等レベルに速い
設置済みの場合は、for setPerm() より圧倒的に両者が速い

// 一定範囲に特定のブロックを設置する関数
function fillCommand(dimensionId, pos1, pos2, blockId, states = "[]") {
    const { x: x1, y: y1, z: z1 } = pos1;
    const { x: x2, y: y2, z: z2 } = pos2;
    world.getDimension(dimensionId).runCommand(`fill ${x1} ${y1} ${z1} ${x2} ${y2} ${z2} ${blockId} ${states}`);
}
// for setPermutation()
function fillScript(dimensionId, start, end, blockId, states = undefined) {
    const minX = Math.min(start.x, end.x); const maxX = Math.max(start.x, end.x);
    const minY = Math.min(start.y, end.y); const maxY = Math.max(start.y, end.y);
    const minZ = Math.min(start.z, end.z); const maxZ = Math.max(start.z, end.z);
    const dimension = world.getDimension(dimensionId);
    let blockPerm;
    if (states) {
        blockPerm = BlockPermutation.resolve(blockId, blockPerm);
    }
    for (let x = minX; x <= maxX; x++) {
        for (let y = minY; y <= maxY; y++) {
            for (let z = minZ; z <= maxZ; z++) {
                const location = { x, y, z };
                if (blockPerm) {
                    dimension.setBlockPermutation(location, blockPerm);
                } else {
                    dimension.setBlockType(location, blockId);
                }
            }
        }
    }
}
// fillBlocks()
function fillScript2(dimensionId, start, end, blockId, states = undefined) {
    const dimension = world.getDimension(dimensionId);
    let blockPerm;
    if (states) {
        blockPerm = BlockPermutation.resolve(blockId, blockPerm);
    }
    dimension.fillBlocks(new BlockVolume(start, end), states ? blockPerm : blockId, {});
}

検証内容 : ブロック検知

特定の座標とディメンションにおいて、指定したブロックIDおよびブロックstatesが存在するかどうかを返すプログラム(10000回)

ブロックstates無
ブロック不一致 command : 341 ms / script 117
ブロック一致  command : 329 ms / script  109

ブロックstates有
ブロック不一致 command : 356 ms / script  193
ブロック一致  command : 321 ms / script  189

Script が優勢

// 座標に特定のブロックがあるかどうかを返す関数
function matchesBlockScript(dimensionId, location, blockId, states = undefined) {
    return world.getDimension(dimensionId).getBlock(location).matches(blockId, states);
}
function matchesBlockCommand(dimensionId, location, blockId, states = "[]") {
    const { x, y, z } = location;
    const result = world.getDimension(dimensionId).runCommand(`execute if block ${x} ${y} ${z} ${blockId} ${states}`);
    return result.successCount === 1;
}

検証用コード

import { system, world } from "@minecraft/server";

system.afterEvents.scriptEventReceive.subscribe(ev => {
    const { id } = ev;
    if (id === "speedTest:run") {
        system.runJob(speedTest(ev.sourceEntity))
    }
});

function* speedTest(player) {
    let commandProcessingTime = 0;
    let scriptProcessingTime = 0;
    // コマンドの処理時間
    commandProcessingTime += getProcessingTime(10000, hasScoreCommand, "test", "test", 1);
    // スクリプトの処理時間
    scriptProcessingTime += getProcessingTime(10000, hasScoreScript, "test", "test", 1);

    world.sendMessage(`Command: ${commandProcessingTime} ms / Script: ${scriptProcessingTime} ms`);
}

function getProcessingTime(count, func, ...args) {
    const startTime = Date.now();
    for (let i = 0; i < count; i++) {
        func(...args);
    }
    const endTime = Date.now();
    return endTime - startTime;
}

※投稿記事に含まれるファイルやリンクにより発生した被害についてクラフターズコロニーは責任を取りません
投稿通報

コメント

コメント通報