使用For循环生成随机视觉噪声

时间:2017-07-29 16:18:52

标签: c++ for-loop random arduino draw

我开始进入C的温和深度,使用arduinos之类的东西,只是想要一些关于我如何使用For循环产生随机噪声的建议。 重要的一点:

if sprite.parent == nil {
    print("sprite has been removed from the parent")
}

该功能在屏幕上逐个绘制一个像素,一旦void testdrawnoise() { int j = 0; for (uint8_t i=0; i<display.width(); i++) { if (i == display.width()-1) { j++; i=0; } M = random(0, 2); // Random 0/1 display.drawPixel(i, j, M); // (Width, Height, Pixel on/off) display.refresh(); } } 达到i,就会向下移动到下一行。像素是显示在(黑色)还是关闭(白色)由display.width()-1确定。

代码工作正常,但我觉得它可以做得更好,或者至少更整洁,也许更有效率。

非常感谢输入和评论。

1 个答案:

答案 0 :(得分:1)

首先,你的循环永远不会结束,并继续无限制递增j,因此,在你填满屏幕一次后,你继续在屏幕高度之外循环;虽然你的库does bounds checking,但是在j溢出并返回到零之前,在没有实际执行有用工作的情况下继续循环使用CPU肯定不是很有效。

此外,签名溢出是C ++中未定义的行为,因此您在技术上处于不稳定的理由(我原本认为Arduino总是使用-fwrapv编译,这保证了有符号整数溢出的循环,but apparently I was mistaken)。 / p>

鉴于您正在使用的库将整个帧缓冲区保留在内存中并在refresh个调用上发送它,所以在每个像素重新发送它没有多大意义 - 特别是因为帧传输是可能是迄今为止这个循环中最慢的部分。所以,你可以将它移出循环。

将它们放在一起(加上缓存宽度和高度并使用random的简单重载),您可以将其更改为:

void testdrawnoise() {
    int w = display.width(), h = display.height();
    for (int j=0; j<h; ++j) {
        for (int i=0; i<w; ++i) {
            display.drawPixel(i, j, random(2));
        }
    }
    display.refresh();
}

(如果您在AVR Arduinos上的屏幕尺寸小于256,那么您可能通过将所有int更改为byte来获得某些东西,但不要相信我的话它)

请注意,这只会执行一次,您可以将其放入loop()函数或无限循环中,以使其继续生成随机模式。

这是您可以使用提供的界面执行的操作;现在,进入无证领域我们可以更快。

如上所述,您正在使用的库将整个帧缓冲区保存在内存中,以每个字节8位打包(按预期方式),在一个名为sharpmem_buffer的全局变量中,初始化with a malloc of the obvious size。< / p>

还应该注意的是,当您在代码中要求一个随机位时,PRNG会生成一个完整的31位随机数,并且只取低位。为什么要浪费所有其他非常好的随机位?

同时,当您调用drawPixel时,库会对内存中的相应字节执行一系列布尔操作,以便仅设置您要求的位而不触及其余位。非常愚蠢,因为你打算用随机的方式覆盖其他的。

所以,把这两个事实放在一起,我们可以做类似的事情:

void testdrawnoise() {
    // access the buffer defined in another .cpp
    extern byte *sharpmem_buffer;
    byte *ptr = sharpmem_buffer; // pointer to current position
    // end position
    byte *end = ptr + display.width()*display.height()/8;
    for (; ptr!=end; ++ptr) {
        // store a full byte of random
        *ptr = random(256);
    }
    display.refresh();
}

,减去refresh()时间,应该至少比前一版本快8倍(我实际上期望更多,因为不仅循环的核心执行迭代的1/8,但它也更简单 - 除了random之外没有函数调用,没有分支,没有内存上的布尔运算。

在AVR Arduinos上,唯一可以进一步优化的点可能是RNG - 我们仍然只使用8位的8位(如果它们实际上是31位?Arduino文档像往常一样糟透了提供有用的技术信息) RNG,所以我们可能在单个RNG调用中生成3个字节的随机数,或者如果我们切换到没有弄乱符号位的手动LCG则为4个字节。在ARM Arduinos上,在最后一种情况下,我们甚至可以通过在内存中执行完整的32位存储而不是写单个字节来获得一些东西。

然而,这些进一步的优化是(1)编写繁琐(如果你必须处理像素数不是24/32的倍数的屏幕)和(2)可能不是特别有利可图,因为大多数无论如何,时间将用于通过SPI进行传输。无论如何值得一提,因为它们可能在其他没有传输瓶颈的情况下有用,可以减慢一切。

鉴于OP的MCU实际上是Cortex M0(因此,32位ARM),因此使用完整的32位PRNG和32位存储器来尝试使其速度更快是值得的。

如上所述,内置random会返回一个有符号的值,但它并不完全清楚它提供的范围;因此,我们必须推出自己的PRNG,保证提供32位完整的随机性。

一个体面且非常快速的PRNG,提供32个随机位,最小状态为xorshift;我们将直接使用维基百科中的xorshift32,因为我们并不需要改进的“*”或“+”版本(我们也不关心更大的时间段提供更大的时间段。)

struct XorShift32 {
    uint32_t state = 0x12345678;
    uint32_t next() {
        uint32_t x = state;
        x ^= x << 13;
        x ^= x >> 17;
        x ^= x << 5;
        state = x;
        return x;
    }
};

XorShift32 xorShift;

现在我们可以重写testdrawnoise()

void testdrawnoise() {
    int size = display.width()*display.height();
    // access the buffer defined in another .cpp
    extern byte *sharpmem_buffer;
    /*
        we can access the framebuffer as if it was an array of 32-bit words;
        this is fine, since it was alloc-ed with malloc, which guarantees memory
        aligned for the most restrictive built-in type, and the library only
        uses it with byte pointers, so there should be no strict aliasing problem
    */
    uint32_t *ptr = (uint32_t *)sharpmem_buffer;
    /*
        notice that the division is an integer division, which truncates; so, we
        are filling the framebuffer up the the last multiple of 4 bytes; with
        "strange" sizes we may be leaving out up to 3 bytes (see later)
    */
    uint32_t *end = ptr + size/32;
    for (; ptr!=end; ++ptr) {
        // store a full byte of random
        *ptr = xorShift.next();
    }
    // now to fill the possibly missing last three bytes
    // pick it up where we left it
    byte *final_ptr = (byte *)end;
    byte *final_end = sharpmem_buffer + size/8;
    // generate 32 random bits; it's ok, we'll need at most 24
    uint32_t r = xorShift.next();
    for(; final_ptr!=final_end; ++final_ptr) {
        // take the lower 8 bits
        *final_ptr = r;
        // throw away the bits we used, get in the upper ones
        r = r>>8;
    }
    display.refresh();
}