Halide示例学习五

算法描述与性能优化的解耦——Halide语言 (1) 解耦工作的难度一定程度上取决于要解耦的两个概念是否能够清晰的区分开来。算法描述和性能优化的解耦是不容易的,因为一般说来这两个概念不易区分。但在图像处理这样的领域里,计算具有典型的模式(数据在pipeline上流动,被各个节点依次处理),我们仍然可以把二者很好地解耦。 Halide就是这样一门语言。 Halide是由MIT、Adobe和Stanford等机构合作实现的图像处理语言,它的核心思想即解耦算法和优化,事实也证明它是成功的,在各种实例中它均以几分之一的代码量实现出同等或者数倍于手工C++代码的效能 阅读详情

函数重定义操作

#include "Halide.h"
#include <stdio.h>

using namespace Halide;

int main(int argc, char **argv) {
    Var x("x"), y("y");
    Buffer<uint8_t> input = load_image("../../images/gray.png");
    {
        // 这里有着var到expr的转换,是一种原始定义
        Func f;
        f(x, y) = x + y;
        // 这里是单点定义
        f(3, 7) = 42;

        // 递归定义:表示在同一位置递归调用本身值f(x,y) = g(f(x,y), var)
        f(x, y) = f(x, y) + 17;

        // 特指某一行进行更新
        f(x, 3) = f(x, 0) * f(x, 10);

        // 特指某一列进行更新
        f(0, y) = f(0, y) / f(3, y);

        // 更新定义的规则是:左右两边的var参数必须是(x,其他),或者是(其他,y),这跟之前的f定义是一致的。
        // 以下部分是合法的
        f(x, 17) = x + 8; // y >= 18
        f(0, y) = y * 8; // x >= 4
        f(x, x + 1) = x + 8; // y >= x + 1
        f(y / 2, y) = f(0, y) * 17; // x >= y / 2

        // 下面的部分是非法的

        // f(x, 0) = f(x + 1, 0);
        // First argument to f on the right-hand-side must be 'x', not 'x + 1'.

        // f(y, y + 1) = y + 8;
        // Second argument to f on the left-hand-side must be 'y', not 'y + 1'.

        // f(y, x) = y - x;
        // Arguments to f on the left-hand-side are in the wrong places.

        // f(3, 4) = x + y;
        // Free variables appear on the right-hand-side but not the left-hand-side.

        // 这里需要根据前面的重定义函数来评估x,y的尺寸关系
        f.realize({100, 101});

        // 每一次的重定义,都会产生多次更新动作,那么在realize的时候将会体现每次的重定义
        Func g("g");
        g(x, y) = x + y;    // Pure definition
        g(2, 1) = 42;       // First update definition
        g(x, 0) = g(x, 1);  // Second update definition

        g.trace_loads();//跟踪读数据
        g.trace_stores();//跟踪写数据

        g.realize({4, 4});
        // 下述是等效C代码
        int result[4][4];
        // Pure definition
        for (int y = 0; y < 4; y++) {
            for (int x = 0; x < 4; x++) {
                result[y][x] = x + y;
            }
        }
        // First update definition
        result[1][2] = 42;
        // Second update definition
        for (int x = 0; x < 4; x++) {
            result[0][x] = result[1][x];
        }
    }

    return 0;
}
# 结果如下所示
Store g.0(0, 0) = 0
Store g.0(1, 0) = 1
Store g.0(2, 0) = 2
Store g.0(3, 0) = 3
Store g.0(0, 1) = 1
Store g.0(1, 1) = 2
Store g.0(2, 1) = 3
Store g.0(3, 1) = 4
Store g.0(0, 2) = 2
Store g.0(1, 2) = 3
Store g.0(2, 2) = 4
Store g.0(3, 2) = 5
Store g.0(0, 3) = 3
Store g.0(1, 3) = 4
Store g.0(2, 3) = 5
Store g.0(3, 3) = 6
Store g.0(2, 1) = 42
Load g.0(0, 1) = 1
Store g.0(0, 0) = 1
Load g.0(1, 1) = 2
Store g.0(1, 0) = 2
Load g.0(2, 1) = 42
Store g.0(2, 0) = 42
Load g.0(3, 1) = 4
Store g.0(3, 0) = 4

RDom下标使用

#include "Halide.h"
#include <stdio.h>

using namespace Halide;

int main(int argc, char **argv) {
    Var x("x"), y("y");
    Buffer<uint8_t> input = load_image("../../images/gray.png");
    // 把更新放置到内部循环中
    {
        // Starting with this pure definition:
        Func f;
        f(x, y) = (x + y) / 100.0f;

        // Say we want an update that squares the first fifty rows. We
        // could do this by adding 50 update definitions:

        // f(x, 0) = f(x, 0) * f(x, 0);
        // f(x, 1) = f(x, 1) * f(x, 1);
        // f(x, 2) = f(x, 2) * f(x, 2);
        // ...
        // f(x, 49) = f(x, 49) * f(x, 49);

        // Or equivalently using a compile-time loop in our C++:
        // for (int i = 0; i < 50; i++) {
        //   f(x, i) = f(x, i) * f(x, i);
        // }

        // 使用RDom变量,我们可以把限定范围的更新组织成循环
        RDom r(0, 50);
        f(x, r) = f(x, r) * f(x, r);
        Buffer<float> halide_result = f.realize({100, 100});

        // See figures/lesson_09_update_rdom.mp4 for a visualization.

        // The equivalent C is:
        float c_result[100][100];
        for (int y = 0; y < 100; y++) {
            for (int x = 0; x < 100; x++) {
                c_result[y][x] = (x + y) / 100.0f;
            }
        }
        for (int x = 0; x < 100; x++) {
            for (int r = 0; r < 50; r++) {
                // RDom这个限定轴,被内嵌为内部循环
                c_result[r][x] = c_result[r][x] * c_result[r][x];
            }
        }

        // Check the results match:
        for (int y = 0; y < 100; y++) {
            for (int x = 0; x < 100; x++) {
                if (fabs(halide_result(x, y) - c_result[y][x]) > 0.01f) {
                    printf("halide_result(%d, %d) = %f instead of %f\n",
                           x, y, halide_result(x, y), c_result[y][x]);
                    return -1;
                }
            }
        }
    }


    return 0;
}

使用RDom计算直方图

#include "Halide.h"
#include <stdio.h>

using namespace Halide;

int main(int argc, char **argv) {
    Var x("x"), y("y");
    Buffer<uint8_t> input = load_image("../../images/gray.png");
    // 现在使用重定义来实现直方图
    {
        Func histogram("histogram");
        // 直方图置零
        histogram(x) = 0;
        // 定义一个多维的归化维度
        RDom r(0, input.width(), 0, input.height());
        // 统计input的直方图(0,255)灰度值之间的个数
        histogram(input(r.x, r.y)) += 1;
        // 只保留[0,255]之间的值
        Buffer<int> halide_result = histogram.realize({256});
        // 等效C代码如下所示
        int c_result[256];
        for (int x = 0; x < 256; x++) {
            c_result[x] = 0;
        }
        for (int r_y = 0; r_y < input.height(); r_y++) {
            for (int r_x = 0; r_x < input.width(); r_x++) {
                c_result[input(r_x, r_y)] += 1;
            }
        }

        // Check the answers agree:
        for (int x = 0; x < 256; x++) {
            if (c_result[x] != halide_result(x)) {
                printf("halide_result(%d) = %d instead of %d\n",
                       x, halide_result(x), c_result[x]);
                return -1;
            }
        }
    }


    return 0;
}

调度函数更新

#include "Halide.h"
#include <stdio.h>

using namespace Halide;

int main(int argc, char **argv) {
    Var x("x"), y("y");
    Buffer<uint8_t> input = load_image("../../images/gray.png");
    // 调度函数更新顺序
    {
        // 对于重定义的函数,我们该如何实现向量化、并行化等操作呢

        // Consider the definition:
        Func f;
        f(x, y) = x * y;
        // Set row zero to each row 8
        f(x, 0) = f(x, 8);
        // Set column zero equal to column 8 plus 2
        f(0, y) = f(8, y) + 2;

        // 原始的 f(x,y) = x * y向量化后再并行化
        f.vectorize(x, 4).parallel(y);
        // 使用Func::update(int upid)来指定第upid次的函数更新的后续优化手段,这里upid=0只能对x那个轴进行向量化
        f.update(0).vectorize(x, 4);
        // upid = 1,表示在第二update公式上进行操作
        Var yo, yi;
        f.update(1).split(y, yo, yi, 4).parallel(yo);//分割后向量化,再接并行

        Buffer<int> halide_result = f.realize({16, 16});

        // See figures/lesson_09_update_schedule.mp4 for a visualization.

        // Here's the equivalent (serial) C:
        int c_result[16][16];

        // Pure step. Vectorized in x and parallelized in y.
        for (int y = 0; y < 16; y++) {  // Should be a parallel for loop
            for (int x_vec = 0; x_vec < 4; x_vec++) {
                int x[] = {x_vec * 4, x_vec * 4 + 1, x_vec * 4 + 2, x_vec * 4 + 3};
                c_result[y][x[0]] = x[0] * y;
                c_result[y][x[1]] = x[1] * y;
                c_result[y][x[2]] = x[2] * y;
                c_result[y][x[3]] = x[3] * y;
            }
        }

        // First update. Vectorized in x.
        for (int x_vec = 0; x_vec < 4; x_vec++) {
            int x[] = {x_vec * 4, x_vec * 4 + 1, x_vec * 4 + 2, x_vec * 4 + 3};
            c_result[0][x[0]] = c_result[8][x[0]];
            c_result[0][x[1]] = c_result[8][x[1]];
            c_result[0][x[2]] = c_result[8][x[2]];
            c_result[0][x[3]] = c_result[8][x[3]];
        }

        // Second update. Parallelized in chunks of size 4 in y.
        for (int yo = 0; yo < 4; yo++) {  // Should be a parallel for loop
            for (int yi = 0; yi < 4; yi++) {
                int y = yo * 4 + yi;
                c_result[y][0] = c_result[y][8] + 2;
            }
        }

        // Check the C and Halide results match:
        for (int y = 0; y < 16; y++) {
            for (int x = 0; x < 16; x++) {
                if (halide_result(x, y) != c_result[y][x]) {
                    printf("halide_result(%d, %d) = %d instead of %d\n",
                           x, y, halide_result(x, y), c_result[y][x]);
                    return -1;
                }
            }
        }
    }


    return 0;
}

多阶段重定义操作

#include "Halide.h"
#include <stdio.h>

using namespace Halide;

int main(int argc, char **argv) {
    Var x("x"), y("y");
    Buffer<uint8_t> input = load_image("../../images/gray.png");
    // 当涉及到多个计算图的时候,重定义操作又是如何进行的呢
    {
        // 对于默认的多计算内敛模式而言,重定义似乎没有意义
        Func producer, consumer;
        producer(x) = x * 2;//纯定义
        producer(x) += 10;//更新
        consumer(x) = 2 * producer(x);//内敛
        Buffer<int> halide_result = consumer.realize({10});

        // See figures/lesson_09_inline_reduction.gif for a visualization.

        // The equivalent C is:
        int c_result[10];
        for (int x = 0; x < 10; x++) {
            int producer_storage[1];
            // Pure step for producer
            producer_storage[0] = x * 2;
            // Update step for producer
            producer_storage[0] = producer_storage[0] + 10;
            // Pure step for consumer
            c_result[x] = 2 * producer_storage[0];
        }

        // Check the results match
        for (int x = 0; x < 10; x++) {
            if (halide_result(x) != c_result[x]) {
                printf("halide_result(%d) = %d instead of %d\n",
                       x, halide_result(x), c_result[x]);
                return -1;
            }
        }
    }

    // 对comsumer这个后计算图进行重定义操作
    // 情况1: consumer只引用producer的纯定义
    {
        Func producer, consumer;

        // The producer is pure.
        producer(x) = x * 17;
        consumer(x) = 2 * producer(x);
        consumer(x) += 50;

        // The valid schedules for the producer in this case are
        // the default schedule - inlined, and also:
        //
        // 1) producer.compute_at(x), which places the computation of
        // the producer inside the loop over x in the pure step of the
        // consumer.
        //
        // 2) producer.compute_root(), which computes all of the
        // producer ahead of time.
        //
        // 3) producer.store_root().compute_at(x), which allocates
        // space for the consumer outside the loop over x, but fills
        // it in as needed inside the loop.
        //
        // Let's use option 1.

        producer.compute_at(consumer, x);

        Buffer<int> halide_result = consumer.realize({10});

        // See figures/lesson_09_compute_at_pure.gif for a visualization.

        // The equivalent C is:
        int c_result[10];
        // compute_at使得prudcer先算,又因为内建的原因,consumer的内建操作使得完成consumer的纯定义的计算
        for (int x = 0; x < 10; x++) {
            // Pure step for producer
            int producer_storage[1];
            producer_storage[0] = x * 17;
            c_result[x] = 2 * producer_storage[0];
        }
        // 然后重定义计算
        for (int x = 0; x < 10; x++) {
            c_result[x] += 50;
        }
        // Check the results match
        for (int x = 0; x < 10; x++) {
            if (halide_result(x) != c_result[x]) {
                printf("halide_result(%d) = %d instead of %d\n",
                       x, halide_result(x), c_result[x]);
                return -1;
            }
        }
    }

    {
        // 情况 2: consumer只在重定义的时候引用producer
        Func producer, consumer;
        producer(x) = x * 17;
        consumer(x) = 100 - x * 10;
        consumer(x) += producer(x);
        producer.compute_at(consumer, x);

        // Note however, that we didn't say:
        //
        // producer.compute_at(consumer.update(0), x).
        //
        // Scheduling is done with respect to Vars of a Func, and
        // the Vars of a Func are shared across the pure and
        // update steps.

        Buffer<int> halide_result = consumer.realize({10});

        // See figures/lesson_09_compute_at_update.gif for a visualization.

        // The equivalent C is:
        int c_result[10];
        // 纯定义
        for (int x = 0; x < 10; x++) {
            c_result[x] = 100 - x * 10;
        }
        // 重定义部分
        for (int x = 0; x < 10; x++) {
            // producer的纯定义
            int producer_storage[1];
            producer_storage[0] = x * 17;
            c_result[x] += producer_storage[0];
        }

        // Check the results match
        for (int x = 0; x < 10; x++) {
            if (halide_result(x) != c_result[x]) {
                printf("halide_result(%d) = %d instead of %d\n",
                       x, halide_result(x), c_result[x]);
                return -1;
            }
        }
    }

    {
        // 情况 3: 在纯定义和重定义的时候都使用了producer的情况
        Func producer, consumer;
        producer(x) = x * 17;
        consumer(x) = 170 - producer(x);
        consumer(x) += producer(x) / 2;
        producer.compute_at(consumer, x);

        Buffer<int> halide_result = consumer.realize({10});

        // See figures/lesson_09_compute_at_pure_and_update.gif for a visualization.
        // 不管如何,中间计算过程都没有专门为了保存producer而设置的中间Buffer
        int c_result[10];
        // Pure step for the consumer
        for (int x = 0; x < 10; x++) {
            // Pure step for producer
            int producer_storage[1];
            producer_storage[0] = x * 17;
            c_result[x] = 170 - producer_storage[0];
        }
        // Update step for the consumer
        for (int x = 0; x < 10; x++) {
            // Another copy of the pure step for producer
            int producer_storage[1];
            producer_storage[0] = x * 17;
            c_result[x] += producer_storage[0] / 2;
        }

        // Check the results match
        for (int x = 0; x < 10; x++) {
            if (halide_result(x) != c_result[x]) {
                printf("halide_result(%d) = %d instead of %d\n",
                       x, halide_result(x), c_result[x]);
                return -1;
            }
        }
    }

    {
        // 情况 4:在多次重定义的过程中才使用producer
        Func producer, consumer;
        producer(x, y) = (x * y) / 10 + 8;
        consumer(x, y) = x + y;
        consumer(x, 0) += producer(x, x);
        consumer(0, y) += producer(y, 9 - y);

        // In this case neither producer.compute_at(consumer, x)
        // nor producer.compute_at(consumer, y) will work, because
        // either one fails to cover one of the uses of the
        // producer. So we'd have to inline producer, or use
        // producer.compute_root().

        // Let's say we really really want producer to be
        // compute_at the inner loops of both consumer update
        // steps. Halide doesn't allow multiple different
        // schedules for a single Func, but we can work around it
        // by making two wrappers around producer, and scheduling
        // those instead:

        // Attempt 2:
        Func producer_1, producer_2, consumer_2;
        producer_1(x, y) = producer(x, y);
        producer_2(x, y) = producer(x, y);

        consumer_2(x, y) = x + y;
        consumer_2(x, 0) += producer_1(x, x);
        consumer_2(0, y) += producer_2(y, 9 - y);

        // The wrapper functions give us two separate handles on
        // the producer, so we can schedule them differently.
        producer_1.compute_at(consumer_2, x);
        producer_2.compute_at(consumer_2, y);

        Buffer<int> halide_result = consumer_2.realize({10, 10});

        // See figures/lesson_09_compute_at_multiple_updates.mp4 for a visualization.

        // The equivalent C is:
        int c_result[10][10];
        // Pure step for the consumer
        for (int y = 0; y < 10; y++) {
            for (int x = 0; x < 10; x++) {
                c_result[y][x] = x + y;
            }
        }
        // First update step for consumer
        for (int x = 0; x < 10; x++) {
            int producer_1_storage[1];
            producer_1_storage[0] = (x * x) / 10 + 8;
            c_result[0][x] += producer_1_storage[0];
        }
        // Second update step for consumer
        for (int y = 0; y < 10; y++) {
            int producer_2_storage[1];
            producer_2_storage[0] = (y * (9 - y)) / 10 + 8;
            c_result[y][0] += producer_2_storage[0];
        }

        // Check the results match
        for (int y = 0; y < 10; y++) {
            for (int x = 0; x < 10; x++) {
                if (halide_result(x, y) != c_result[y][x]) {
                    printf("halide_result(%d, %d) = %d instead of %d\n",
                           x, y, halide_result(x, y), c_result[y][x]);
                    return -1;
                }
            }
        }
    }

    {
        // Case 5: 当使用了RDom限定下标的时候,使用compute_at

        // We are not just restricted to scheduling producers at
        // the loops over the pure variables of the consumer. If a
        // producer is only used within a loop over a reduction
        // domain (RDom) variable, we can also schedule the
        // producer there.

        Func producer, consumer;

        RDom r(0, 5);
        producer(x) = x % 8;
        consumer(x) = x + 10;
        consumer(x) += r + producer(x + r);

        producer.compute_at(consumer, r);

        Buffer<int> halide_result = consumer.realize({10});

        // See figures/lesson_09_compute_at_rvar.gif for a visualization.

        // The equivalent C is:
        int c_result[10];
        // Pure step for the consumer.
        for (int x = 0; x < 10; x++) {
            c_result[x] = x + 10;
        }
        // Update step for the consumer.
        for (int x = 0; x < 10; x++) {
            // The loop over the reduction domain is always the inner loop.
            for (int r = 0; r < 5; r++) {
                // We've schedule the storage and computation of
                // the producer here. We just need a single value.
                int producer_storage[1];
                // Pure step of the producer.
                producer_storage[0] = (x + r) % 8;

                // Now use it in the update step of the consumer.
                c_result[x] += r + producer_storage[0];
            }
        }

        // Check the results match
        for (int x = 0; x < 10; x++) {
            if (halide_result(x) != c_result[x]) {
                printf("halide_result(%d) = %d instead of %d\n",
                       x, halide_result(x), c_result[x]);
                return -1;
            }
        }
    }


    return 0;
}

使用归化函数进行模糊操作

#include "Halide.h"
#include <stdio.h>

using namespace Halide;

int main(int argc, char **argv) {
    Var x("x"), y("y");
    Buffer<uint8_t> input = load_image("../../images/gray.png");
    // 下面是一个模糊的归化函数
    {
        // 下述是一个边界限定的函数
        Func clamped = BoundaryConditions::repeat_edge(input);
        // 定义一个从该点开始的相对位置为-2的点
        RDom r(-2, 5, -2, 5);
        // 计算每个像素值的附近的和
        Func local_sum;
        local_sum(x, y) = 0;// 纯定义
        local_sum(x, y) += clamped(x + r.x, y + r.y); // 归约求和
        // 求平均值,然后int32->uint8
        Func blurry;
        blurry(x, y) = cast<uint8_t>(local_sum(x, y) / 25);
        Buffer<uint8_t> halide_result = blurry.realize({input.width(), input.height()});

        // 默认的调度就是把clamped进行了内联
        // 等效C代码如下
        Buffer<uint8_t> c_result(input.width(), input.height());
        for (int y = 0; y < input.height(); y++) {
            for (int x = 0; x < input.width(); x++) {
                int local_sum[1];
                // Pure step of local_sum
                local_sum[0] = 0;
                // Update step of local_sum
                for (int r_y = -2; r_y <= 2; r_y++) {
                    for (int r_x = -2; r_x <= 2; r_x++) {
                        // The clamping has been inlined into the update step.
                        int clamped_x = std::min(std::max(x + r_x, 0), input.width() - 1);
                        int clamped_y = std::min(std::max(y + r_y, 0), input.height() - 1);
                        local_sum[0] += input(clamped_x, clamped_y);
                    }
                }
                // Pure step of blurry
                c_result(x, y) = (uint8_t)(local_sum[0] / 25);
            }
        }

        // Check the results match
        for (int y = 0; y < input.height(); y++) {
            for (int x = 0; x < input.width(); x++) {
                if (halide_result(x, y) != c_result(x, y)) {
                    printf("halide_result(%d, %d) = %d instead of %d\n",
                           x, y, halide_result(x, y), c_result(x, y));
                    return -1;
                }
            }
        }
    }


    return 0;
}

内置的归化函数sum

#include "Halide.h"
#include <stdio.h>

using namespace Halide;

int main(int argc, char **argv) {
    Var x("x"), y("y");
    Buffer<uint8_t> input = load_image("../../images/gray.png");
    // 归约函数sum
    {
        // Halide.h中包含了sum归约函数
        Func f1;
        RDom r(0, 100);
        f1(x) = sum(r + x) * 7;
        // 上述操作等下等效
        Func f2;
        Func anon;
        anon(x) = 0;
        anon(x) += r + x;//固定x,那么对于每一个r[0,100],我们必须计算anon[x] = [x + r for r in [0,100]]
        f2(x) = anon(x) * 7;

        // 所以尽管f1使用了sum函数,但是这个依然是个纯定义函数
        Buffer<int> halide_result_1 = f1.realize({10});
        Buffer<int> halide_result_2 = f2.realize({10});

        // The equivalent C is:
        int c_result[10];
        for (int x = 0; x < 10; x++) {
            int anon[1];
            anon[0] = 0;
            for (int r = 0; r < 100; r++) {
                anon[0] += r + x;
            }
            c_result[x] = anon[0] * 7;
        }

        // Check they all match.
        for (int x = 0; x < 10; x++) {
            if (halide_result_1(x) != c_result[x]) {
                printf("halide_result_1(%d) = %d instead of %d\n",
                       x, halide_result_1(x), c_result[x]);
                return -1;
            }
            if (halide_result_2(x) != c_result[x]) {
                printf("halide_result_2(%d) = %d instead of %d\n",
                       x, halide_result_2(x), c_result[x]);
                return -1;
            }
        }
    }


    return 0;
}

使用归约函数的一个复杂例子

#include "Halide.h"
#include <stdio.h>

using namespace Halide;

int main(int argc, char **argv) {
    Var x("x"), y("y");
    Buffer<uint8_t> input = load_image("../../images/gray.png");
    // 一个使用归约函数的复杂例子
    {
        // 其他的归约函数如下所示:
        // 累乘"product", 最小值"minimum",
        // 最大值"maximum", 反解最小"argmin", 反解最大"argmax". 

        // 首先进行边界限定
        Func clamped;
        Expr x_clamped = clamp(x, 0, input.width() - 1);
        Expr y_clamped = clamp(y, 0, input.height() - 1);
        clamped(x, y) = input(x_clamped, y_clamped);

        RDom box(-2, 5, -2, 5);
        // 然后进行如下归约
        Func spread;
        spread(x, y) = (maximum(clamped(x + box.x, y + box.y)) -
                        minimum(clamped(x + box.x, y + box.y)));

        // 循环拆分、并行化
        Var yo, yi;
        spread.split(y, yo, yi, 32).parallel(yo);

        // 使用向量化,maximum和minimum归约函数也会受到向量化影响
        spread.vectorize(x, 16);

        // 为了放置边界被多次计算,这里使用了store_at和compute_at
        clamped.store_at(spread, yo).compute_at(spread, yi);

        Buffer<uint8_t> halide_result = spread.realize({input.width(), input.height()});

// 这里使用优化的C代码和Halide代码进行比较,来看两者的速度差别
#define __SSE2__
#define _OPENMP

#ifdef __SSE2__
        // Don't include the time required to allocate the output buffer.
        Buffer<uint8_t> c_result(input.width(), input.height());

#ifdef _OPENMP
        double t1 = current_time();
#endif

        // Run this one hundred times so we can average the timing results.
        for (int iters = 0; iters < 100; iters++) {

#pragma omp parallel for
            for (int yo = 0; yo < (input.height() + 31) / 32; yo++) {
                int y_base = std::min(yo * 32, input.height() - 32);

                // Compute clamped in a circular buffer of size 8
                // (smallest power of two greater than 5). Each thread
                // needs its own allocation, so it must occur here.

                int clamped_width = input.width() + 4;
                uint8_t *clamped_storage = (uint8_t *)malloc(clamped_width * 8);//中间计算结果,store_at

                for (int yi = 0; yi < 32; yi++) {//循环拆分的内层循环
                    int y = y_base + yi;

                    uint8_t *output_row = &c_result(0, y);

                    // compute_at的效用,使得在向量化前计算了clamped
                    int min_y_clamped = (yi == 0) ? (y - 2) : (y + 2);
                    int max_y_clamped = (y + 2);
                    for (int cy = min_y_clamped; cy <= max_y_clamped; cy++) { //compute_at,用于进行计算操作
                        // Figure out which row of the circular buffer
                        // we're filling in using bitmasking:
                        uint8_t *clamped_row =
                            clamped_storage + (cy & 7) * clamped_width;

                        // Figure out which row of the input we're reading
                        // from by clamping the y coordinate:
                        int clamped_y = std::min(std::max(cy, 0), input.height() - 1);
                        uint8_t *input_row = &input(0, clamped_y);

                        // Fill it in with the padding.
                        for (int x = -2; x < input.width() + 2; x++) {
                            int clamped_x = std::min(std::max(x, 0), input.width() - 1);
                            *clamped_row++ = input_row[clamped_x];
                        }
                    }

                    // 现在进行最终的计算
                    for (int x_vec = 0; x_vec < (input.width() + 15) / 16; x_vec++) {
                        int x_base = std::min(x_vec * 16, input.width() - 16);

                        // Allocate storage for the minimum and maximum
                        // helpers. One vector is enough.
                        __m128i minimum_storage, maximum_storage;

                        // The pure step for the maximum is a vector of zeros
                        maximum_storage = _mm_setzero_si128();

                        // maximum操作
                        for (int max_y = y - 2; max_y <= y + 2; max_y++) {
                            uint8_t *clamped_row =
                                clamped_storage + (max_y & 7) * clamped_width;
                            for (int max_x = x_base - 2; max_x <= x_base + 2; max_x++) {
                                __m128i v = _mm_loadu_si128(
                                    (__m128i const *)(clamped_row + max_x + 2));
                                maximum_storage = _mm_max_epu8(maximum_storage, v);
                            }
                        }

                        // 定义一个0值的minimum
                        minimum_storage = _mm_cmpeq_epi32(_mm_setzero_si128(),
                                                          _mm_setzero_si128());
                        // minmum操作
                        for (int min_y = y - 2; min_y <= y + 2; min_y++) {
                            uint8_t *clamped_row =
                                clamped_storage + (min_y & 7) * clamped_width;
                            for (int min_x = x_base - 2; min_x <= x_base + 2; min_x++) {
                                __m128i v = _mm_loadu_si128(
                                    (__m128i const *)(clamped_row + min_x + 2));
                                minimum_storage = _mm_min_epu8(minimum_storage, v);
                            }
                        }

                        // sse减法
                        __m128i spread = _mm_sub_epi8(maximum_storage, minimum_storage);

                        // sse存储
                        _mm_storeu_si128((__m128i *)(output_row + x_base), spread);
                    }
                }

                free(clamped_storage);
            }
        }

// Skip the timing comparison if we don't have openmp
// enabled. Otherwise it's unfair to C.
#ifdef _OPENMP
        double t2 = current_time();

        // Now run the Halide version again without the
        // jit-compilation overhead. Also run it one hundred times.
        for (int iters = 0; iters < 100; iters++) {
            spread.realize(halide_result);
        }

        double t3 = current_time();
        printf("Halide spread took %f ms. C equivalent took %f ms\n",
               (t3 - t2) / 100, (t2 - t1) / 100);

#endif  // _OPENMP

        // Check the results match:
        for (int y = 0; y < input.height(); y++) {
            for (int x = 0; x < input.width(); x++) {
                if (halide_result(x, y) != c_result(x, y)) {
                    printf("halide_result(%d, %d) = %d instead of %d\n",
                           x, y, halide_result(x, y), c_result(x, y));
                    return -1;
                }
            }
        }

#endif  // __SSE2__
    }


    return 0;
}
# 结果如下所示
Halide spread took 0.668330 ms. C equivalent took 68.161500 ms
Halide简介 Halide是用C++作为宿主语言的一个图像处理相关的DSL(Domain Specified Language)语言,全称领域专用语言。主要的作用为在软硬层面上(与算法本身的设计无关)实现对算法的底层加速,我们有必要对其有一定的了解。因为不论是 传统的图像处理方法亦或是深度学习应用 都使用到了halide的思想。 来自 https://www.codercto.com/a/67796.html ... 阅读详情

相关推荐

Halide 构建

hello halide 随着人工智能,VR等领域的发展,我们对于高性能图像处理程序的需求越来越大;当前编程工具开发的高性能图像处理程序牺牲了程序的可读性,模块性,可移植性等;其主要原因是,当前程序将算法描述, 数据存储以及计算糅杂在一起。同时手工编写高性能图像处理程序开发效率较低,难以快速构建大量高性能图像处理代码。 在这样的背景下,MIT的研究人员专门为图像处理设计了一种程序语言——Halid...

2Know的博客 1765

halide编程技术指南(连载十)

本文是halide编程指南的连载,已同步至公众号 第17章: 非矩形域上的约化 // 本课程演示如何使用谓词定义迭代简化域子集的更新(难懂). // linux: // g++ lesson_17*.cpp -g -I <path/to/Halide.h> -L <path/to/libHalide.so> -lHalide -lpthread -ldl -o lesson_17 -std=c++11 // LD_LIBRARY_PATH=<path/to/libH.

Aoulun的博客 618

Halide编译安装与基本数据结构

Overview Halide是为了在现代计算机上编写更高性能的图像处理代码而设计的一种编程语言。目前只吃的目标为: CPU架构:X86, ARM, MIPS, Hexagon, PowerPC 操作系统:Linux, Windows, macOS, Andriod, IOS, Qualcomm QuRT GPU计算API: CUDA,OpenGL, OpenCL, Apple Metal, Microsoft Direction X 12 Halide不是独立的一种编程语言,而是嵌入在C++中,这

亦梦云烟的博客 1745

Halide示例学习一

Halide示例学习一示例学习基本变量和概念操控图片操作debug操作1 示例学习 基本变量和概念 // 使用Halide,我们只需要包含Halide.h这个头文件就可以了 #include "Halide.h" #include <stdio.h> int main(int argc, char **argv) { // Halide::Func代表了一个计算图,代表了每一个像素值需要进行的计算操作 Halide::Func gradient; // Halide::

Pengcode的博客 763

windows安装Halide

https://github.com/halide/Halide/releases 下载Halide压缩包,解压 添加环境变量,C:\Opencv3.4\halide\Debug;C:\Opencv3.4\halide\Release ①Linker>general>Adiditional Library Directories release C:\lib\halide\Releas...

勉強 1711

使用Halide后端提高OpenCV效率的方法

通过上述步骤,我们成功地在OpenCV中启用了Halide后端,并使用Halide后端进行了高斯滤波操作。通过上述步骤,我们成功地在OpenCV中启用了Halide后端,并使用Halide后端进行了高斯滤波操作。通过使用Halide后端,你可以进一步优化和并行化你的图像处理算法,以实现更高的性能。通过使用Halide后端,你可以进一步优化和并行化你的图像处理算法,以实现更高的性能。要启用OpenCV的Halide后端,首先确保已经正确安装了OpenCV和Halide,并配置了正确的编译环境。

PixelNovaO的博客 314

halide编程技术指南(连载五)

本文是halide编程指南的连载,已同步至公众号 第九章多重传递函数、更新定义和约化 #include "Halide.h" #include <stdio.h> // 我们将在本课使用x86 SSE内部函数. #ifdef __SSE2__ #include <emmintrin.h> #endif // 最后我们还需要一个时钟来做性能测试. #include "clock.h" using namespace Halide; // 加载PNG的支持代码. #incl.

Aoulun的博客 710

OpenCV如何启用Halide后端以提高效率

OpenCV如何启用Halide后端以提高效率如何启用Halide后端以提高效率介绍要求LLVM编译器Halide语言使用Halide后端构建OpenCV将Halide设置为首选后端 如何启用Halide后端以提高效率 介绍 本教程指导如何使用Halide语言后端在OpenCV深度学习模块中运行模型。Halide是一个开源项目,可以让我们以易于阅读的格式编写图像处理算法,根据特定设备安排计算时间,并以相当高的效率对其进行评估。 要求 LLVM编译器 LLVM编译可能需要很长时间。 从http://relea

希望我的博客,能帮上你解决学习中工作中所遇到的问题 1006

Halide学习笔记----Halide tutorial源码阅读13

Halide入门13// Halide tutorial lesson 13: Tuples // Halide入门第13课:元组// This lesson describes how to write Funcs that evaluate to multiple // values. // 本科介绍如何编写多值函数// On linux, you can compile and run it

luzhanbo207的博客 1295

在AutoDL的A100云服务器上,用Docker搞定Sapien+RobotWin环境(附完整conda list)

本文详细介绍了在AutoDL的A100云服务器上使用Docker构建Sapien+RobotWin环境的完整指南。通过配置Vulkan渲染管线、精确复现Conda环境以及解决常见问题,帮助研究人员高效搭建稳定的机器人仿真开发环境,特别适合需要高性能GPU加速的复杂仿真任务。

weixin_30689307的博客 431

HMC7044芯片模拟SPI配置

摘要: HMC7044是一款高性能双环路整数N分频抖动衰减器,支持SPI接口配置,适用于高速数据转换器时钟系统。该芯片采用双PLL级联架构,具有14路可配置输出,可满足GSM/LTE基站设计需求。SPI接口采用3线模式(SLEN/SCLK/SDATA),支持24位寄存器读写(13位地址+8位数据)。驱动实现基于Zynq EMIO模拟SPI,提供初始化、数据传输及寄存器配置功能,支持三线制工作模式和多种时钟参数设置。

toonyhe的博客 1094

Halide学习

目的:在现代机器上完成高性能的图像处理。 编译器目标:x86/SSE, ARM V7/NEON, CUDA, Native Client, OpenCL, and Metal. 大致流程:用Halide::Var, Halide::Expr, Halide::Func类型的对象完成c++程序,然后调用Halide::Func::compile_to_file来生成对象文件和头文件。或者调用Halide::Func::realize即时编译(JIT)并即刻运行整套pipeline,这有利于测试小的程序。

u011994454的博客 742

MySQL8.2.0下载安装

4、点击No thanks,just start my download,就是只是开始下载的意思,点击下载,等待下载完成。5、新建空白行,或直接双击紧挨的空白行,把复制的mysql的安装路径bin路径复制粘贴过来,确定,确定,确定。1、上面点击finish后,自动勾选了run mysql配置,自动会打开配置页面,点击Next。1、双击安装包,进入安装流程,勾选我同意 I accept,next。2、根据自己需要选择,我选的第二个,可以自己改安装路径。2、默认,点击Next,记住默认端口3306。

m0_59681797的博客 1万+

Halide示例学习二

Halide的调度实操以及说明

Pengcode的博客 587

别再只会画方块了!Cesium 1.62 实战:用 Polygon、Ellipse 和 PolylineVolume 玩转复杂地理围栏与3D管道

本文深入探讨了Cesium 1.62在复杂地理围栏与3D管道开发中的高级应用,通过Polygon、Ellipse和PolylineVolume等API实现不规则禁飞区建模、椭圆围栏设计及地下管线可视化。文章结合实战代码,详细解析了性能优化技巧和工程实践,帮助开发者突破基础图形绘制局限,构建真实业务场景中的三维空间系统。

weixin_26763955的博客 104

Neo4j 智能供应链应用源代码简析

本文简析Neo4j智能供应链应用的源代码,让你也可以快速开发自己的供应链应用。

Neo4j 开发者博客 1679

为Halide安装opencl支持

打开TargetOpenCL开关,我们可以看到使用opencl进行推理的时候,计算时调用的openclapi是如何的。展示的测速结果如下所示,这里可以看出来,虽然都是使用了cpu进行计算,但是还是intel-cpu-opencl速度更快些。compile_jit进行处理操作。查看主机平台支持的设备平台。设置kernel参数。...

Pengcode的博客 715

Cadence OrCAD: 层次化设计 Hierarchical Design

详细介绍OrCAD的原理图层次化设计方法。

Fantasy237的博客 1万+

Halide示例学习四

Halide示例学习四多阶段计算图多计算图组合示例compute_at使用方法store_root配合compute_at使用方法tile配合compute_at使用方法compute_at和store_at以及之前的混合技使用方法 多阶段计算图 #include "Halide.h" #include <stdio.h> using namespace Halide; int main(int argc, char **argv) { Var x("x"), y("y");

Pengcode的博客 437

[DDR5]DDR5 SDRAM 状态图 和 基本功能

DDR5 SDRAM的读写操作以突发(burst mode)为导向,从选定的位置开始,并按照编程顺序进行16个突发长度或8个“切片”突发长度的操作。从ACTIVATE命令开始,然后是读取或写入命令。

串起点滴 1200

泛微OA集成阿里云短信(E9保姆教程)

泛微OA集成阿里云短信(E9保姆教程)

技术分享 515

Halide示例学习三

Halide示例学习三改变维度遍历起点多计算图组合示例 改变维度遍历起点 #include "Halide.h" #include <stdio.h> using namespace Halide; int main(int argc, char **argv) { Func gradient("gradient"); Var x("x"), y("y"); gradient(x, y) = x + y; gradient.trace_stores();

Pengcode的博客 296

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

如何为 Halide 后端安排网络

介绍我们使用的每台设备都是一样的卤化物代码。但是为了达到令人满意的效率,我们应该正确地安排计算。在本教程中,我们将介绍在 OpenCV 深度学习模块中使用 Halide 后端调度网络的方法。为了更好地理解 Halide 调度,您可能需要阅读教程 @。如果这是你第一次在 OpenCV 中与 Halide 会面,我们建议从开始。配置文件您可以通过编写文本配置文件来计划卤化物管道的计算。这意味着您可以轻松地矢量化、并行化和管理层计算的循环顺序。

2301_81888214的博客 927

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

(深度学习优化大师)TVM的前辈,介绍下什么是HALIDE

在开始学习之前推荐大家可以多在FlyAI竞赛服务平台多参加训练和竞赛,以此来提升自己的能力。FlyAI是为AI开发者提供数据竞赛并支持GPU离线训练的一站式服务平台。每周免费提供项目开源算法样例,支持算法能力变现以及快速的迭代算法模型。 前言 前几天又用到了TVM,想着是否能够间接替代TensorRT作为GPU服务器部署的一个后端。TensorRT在自家的GPU上确实强大,也是老黄家重点关照的”开源项目“。但TensorRT还是有一点点小缺点的: 经过infer优化后的模型与特定GPU绑定,...

iFlyAI的博客 3284

Halide源码编译

Halide源码编译Halide为何要源码编译?源码编译LLVM库源码编译Halide测试Halide是否编译成功生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 Halide   Halide 算是一种编程模式,主要用于在现代机器上来进行高性能的图像和数组处理。其寄生在C++编程语言中,作为DSL编程语言(特定域语言)来实现高

Pengcode的博客 1942

快速入门Halide

Halide代码流程: 声明函数,变量,表达式 用变量、表达式等实现算法 使用调度策略对算法进行调度调优 调用函数的realize成员函数,对函数进行实现 写出数据 example 1: Halide::Func gradient; // Func对象表示了一个pipeline阶段。它是一个纯函数,定义了每个像素点对应的值。 Halide::Var x, y; // Var对象是Func的定义域,或者说是Func的参数。它们本身没有任何意义。Var用来索引 Halide::Expr e = x

Time_remember的专栏 1290
上一篇: Halide示例学习四
下一篇: Intel-Cpu-OpenCL-Runtime-SDK的使用和示例源码编译
慷仔
博客等级 码龄9年 88粉丝 46原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值