Измерение времени с tf.time

В библиотеке TensorFlow.js tf.time предоставляет мощный инструмент для измерения времени выполнения операций и оценки производительности модели или отдельных вычислительных блоков. Он особенно полезен при оптимизации кода и анализе узких мест в вычислениях на CPU или GPU.


Основы работы с tf.time

Метод tf.time принимает на вход функцию, внутри которой выполняются тензорные операции TensorFlow.js. Результатом вызова является объект с двумя основными свойствами:

  • kernelMs — время выполнения низкоуровневых операций (ядровых функций) в миллисекундах. Этот показатель отражает реальное время, затраченное на вычисления тензоров.
  • wallMs — «стеновое» время, общее время выполнения всей функции, включая накладные расходы JavaScript и асинхронную обработку. Обычно wallMs чуть больше kernelMs, так как учитывает дополнительные операции управления.

Пример базового использования:

const result = await tf.time(() => {
    const a = tf.randomNormal([1000, 1000]);
    const b = tf.randomNormal([1000, 1000]);
    return tf.matMul(a, b);
});

console.log(`Kernel time: ${result.kernelMs} ms`);
console.log(`Wall time: ${result.wallMs} ms`);

В этом примере создаются два тензора размером 1000×1000 и выполняется их умножение. Время выполнения фиксируется отдельно для ядра и для общей функции.


Асинхронная природа измерений

TensorFlow.js выполняет многие операции асинхронно, особенно на WebGL и WebGPU. Это означает, что замеры wallMs могут быть полезны для оценки полной задержки, а kernelMs точнее отражает нагрузку на GPU или CPU.

При необходимости точного измерения асинхронных операций внутри tf.time можно использовать tf.nextFrame() для синхронизации визуализации или завершения вычислений:

await tf.time(async () => {
    const x = tf.randomNormal([500, 500]);
    const y = tf.randomNormal([500, 500]);
    const z = tf.matMul(x, y);
    await z.data(); // Принудительное ожидание завершения вычислений
});

Вызов await z.data() гарантирует, что все операции будут завершены до окончания измерения времени.


Использование tf.time для оптимизации

  1. Сравнение разных реализаций операций При создании моделей или вычислительных пайплайнов можно тестировать альтернативные методы реализации и выбирать наиболее быстрые. Например, сравнение последовательного сложения и батчевой операции:

    const resultSeq = await tf.time(() => {
        let sum = tf.zeros([1000, 1000]);
        for (let i = 0; i < 10; i++) {
            sum = tf.add(sum, tf.randomNormal([1000, 1000]));
        }
        return sum;
    });
    
    const resultBatch = await tf.time(() => {
        const tensors = Array.from({ length: 10 }, () => tf.randomNormal([1000, 1000]));
        return tf.addN(tensors);
    });
    
    console.log(`Sequential: ${resultSeq.kernelMs} ms, Batch: ${resultBatch.kernelMs} ms`);

    Это позволяет выявить, что в большинстве случаев пакетные операции (tf.addN) выполняются быстрее за счет оптимизации ядра и меньших накладных расходов JavaScript.

  2. Мониторинг горячих участков кода В больших моделях можно оборачивать ключевые слои в tf.time для локальной оптимизации. Например, измерение времени работы слоя Conv2D:

    const convTime = await tf.time(() => {
        const input = tf.randomNormal([1, 128, 128, 3]);
        const kernel = tf.randomNormal([3, 3, 3, 16]);
        return tf.conv2d(input, kernel, 1, 'same');
    });
    
    console.log(`Conv2D layer kernel time: ${convTime.kernelMs} ms`);

    Такой подход позволяет понять, какие слои или операции создают узкие места в производительности.


Практические советы

  • Для краткосрочных измерений достаточно вызывать tf.time один раз, но для статистически надежных результатов лучше проводить несколько прогонов и усреднять значения.
  • tf.time подходит для измерения операций с тензорами, но не для всего JavaScript-кода. Асинхронные вызовы вне TensorFlow.js учитываются только в wallMs.
  • На GPU результаты kernelMs могут быть меньше, чем wallMs, из-за асинхронного характера WebGL/WebGPU. Для точного анализа рекомендуется использовать await tensor.data() или tensor.array() внутри замера.
  • Объединение tf.time с профилировщиком браузера помогает выявлять не только вычислительные узкие места, но и узкие места в управлении памятью.

Совместимость с профилированием

TensorFlow.js также предоставляет tf.profile, который позволяет измерять использование памяти и количество операций. В комбинации с tf.time это даёт полное понимание производительности:

const profile = await tf.profile(() => {
    const x = tf.randomNormal([1000, 1000]);
    const y = tf.randomNormal([1000, 1000]);
    return tf.matMul(x, y);
});

console.log(`Memory usage: ${profile.newBytes} bytes, Kernel time: ${profile.kernelMs} ms`);

Использование tf.time совместно с профайлингом делает процесс оптимизации моделей прозрачным и управляемым.


tf.time является важным инструментом для точного анализа производительности TensorFlow.js, позволяя выделять узкие места, сравнивать подходы и планировать оптимизацию кода для CPU и GPU.