【问题标题】:How can I optimize this image copy function for an embedded system如何为嵌入式系统优化此图像复制功能
【发布时间】:2011-05-17 20:29:43
【问题描述】:

下面的函数使用 read_page(pageIter, pageArr, PAGESIZE) 一次读取一页图像,并在 DOUT 和 CCLK 引脚上输出数据。

有人告诉我它效率低下,但我似乎找不到让它更快的方法。它基本上是一个管道,在 64 针 uProcessor 上运行,位于两个内存空间之间。一个持有图像,另一个接收图像。

我使用了 register 关键字,删除了数组索引并用指针算术替换,但它需要更快。

谢谢!

/*
Port C Pin Out
*/
#define     BIT0        0x01    // CCLK
#define     BIT1        0x02    // CS_B
#define     BIT2        0x04    // INIT_B
#define     BIT3        0x08    // PROG_B
#define     BIT4        0x10    // RDRW_B
#define     BIT5        0x20    // BUSY_OUT
#define     BIT6        0x40    // DONE
#define     BIT7        0x80    // DOUT (DIN)

/*
PAGE
*/

#define     PAGESIZE    1024    // Example

void copyImage(ulong startAddress, ulong endAddress)
  {
  ulong pageIter;
  uchar *eByte, *byteIter, pageArr[PAGESIZE];
  register uchar bitIter, portCvar;
  portCvar = PORTC;
  /* Loops through pages in an image using ulong type*/
  for(pageIter = startAddress ;  pageIter <= endAddress ; pageIter += PAGESIZE)
    {
    read_page(pageIter, pageArr, PAGESIZE);
    eByte = pageArr+PAGESIZE;
    /* Loops through bytes in a page using pointer to uchar (pointer to a byte)*/
    for(byteIter = pageArr; byteIter <= eByte; byteIter++)
      {
      /* Loops through bits in byte and writes to PORTC - DIN ANC CCLK  */
      for(bitIter = 0x01; bitIter != 0x00; bitIter = bitIter << 1)
        {
        PORTC = portCvar | BIT0;
        (bitIter & *byteIter) ? (PORTC = portCvar & ~BIT7) : (PORTC = portCvar | BIT7);
        PORTC = portCvar & ~BIT0;
        }
      }
    }
  }

【问题讨论】:

  • 我在这个循环中错过了什么微妙之处:for(pageIter = aAddress ; pageIter &lt;= eAddress ; pageIter += PAGESIZE)?这根本不是一个循环,是吗?
  • @Chris:我修正了一点缩进,如果您不喜欢它,请随时回滚更改。
  • @bitmask:这是一个完全有效的循环,它从aAddresseAddress 迭代内存,步长为PAGESIZE
  • 啊,抱歉,我得矫正视力了,我把eAddress 误读成了aAddress——所以差值就是0(因此说它毫无意义)。
  • 改为 startAddress 和 endAddress 以提高可读性

标签: c image optimization embedded


【解决方案1】:

也许你可以通过像这样展开每个字节的传输来更快

PORTC = clock_1; PORTC = (value & 0x01 ? data1 : data0); PORTC = clock_0;
PORTC = clock_1; PORTC = (value & 0x02 ? data1 : data0); PORTC = clock_0;
PORTC = clock_1; PORTC = (value & 0x04 ? data1 : data0); PORTC = clock_0;
PORTC = clock_1; PORTC = (value & 0x08 ? data1 : data0); PORTC = clock_0;
PORTC = clock_1; PORTC = (value & 0x10 ? data1 : data0); PORTC = clock_0;
PORTC = clock_1; PORTC = (value & 0x20 ? data1 : data0); PORTC = clock_0;
PORTC = clock_1; PORTC = (value & 0x40 ? data1 : data0); PORTC = clock_0;
PORTC = clock_1; PORTC = (value & 0x80 ? data1 : data0); PORTC = clock_0;

在图像循环之外进行一次预计算后

unsigned char clock_1 = portC | BIT0;
unsigned char clock_0 = portC & ~BIT0;
unsigned char data1 = portC | BIT7;
unsigned char data0 = portC & ~BIT7;

【讨论】:

  • 哦,作为一个额外的功能,所有字节中的位现在都反转了。原始代码先发送最低有效位;)
  • @Lindydancer:你说得对,不知何故我误读了原始代码是先发送 MSB。固定的。顺便说一句,由于缺少取消引用,原始代码正在发送地址位...
【解决方案2】:
/* Loops through bits in byte and writes to PORTC - DIN ANC CCLK  */
      for(bitIter = 0x01; bitIter <= 0x80; bitIter = bitIter << 1)
        {
    PORTC = portC | BIT0;
    (bitIter & byteIter) ? (PORTC = portC & ~BIT7) : (PORTC = portC | BIT7);
    PORTC = portC & ~BIT0;
    }

那个循环是关键。我会用生产优化标志编译它,然后查看反汇编。编译器可能会做各种聪明的事情,比如展开循环或简化循环条件。如果我不喜欢在那里看到的内容,我会开始调整 C 代码以帮助编译器找到一个好的优化。如果这被证明是不可能的,那么我可能会使用内联汇编来获得我想要的。

假设我们可以尽可能快地进行(并且循环中的延迟不考虑接收器的设置保持时间),那么我希望将该循环减少到尽可能少的指令。您能否同时设置 BIT0 和数据位,或者这会对接收器造成危险吗?如果可以的话,那将节省一两条指令。许多微优化将依赖于特定的指令集。如果数据有很多 0 或 0xFF,您可以制作特殊的展开情况,其中数据位不改变并且 BIT0 切换 8 次。您可以为单个 nybble 制作 16 个展开的案例,然后为每个字节切换两次。

【讨论】:

    【解决方案3】:
    /* Loops through bits in byte and writes to PORTC - DIN ANC CCLK  */
    for(bitIter = 0x01; bitIter <= 0x80; bitIter = bitIter << 1)
    {
        PORTC = portC | BIT0;
        (bitIter & byteIter) ? (PORTC = portC & ~BIT7) : (PORTC = portC | BIT7);
        PORTC = portC & ~BIT0;
    }
    

    首先,这个循环被打破了。 bitIter 是一个 uchar (我假设它是一个无符号的 8 位字符)。通过将其向左移动,最终将获得预期最终迭代的值 0x80。在下一个班次之后,它将获得值 0。

    关于效率。根据架构,执行PORTC = PORTC | BIT0 操作可能会导致单个位集。但是,它也可能导致读取、设置寄存器中的位和存储。

    如前所述,如果可能,请尝试同时设置 BIT0 和 BIT7(如果硬件允许的话)。

    我会尝试类似:

    bitIter = 0x01;
    do
    {
      if (byteIter & bitIter)
      {
        PORTC = BIT0;
      }
      else
      {
        PORTC = (BIT0 | BIT7);
      }
      PORTC = 0;
    
      bitIter <<= 1;
    } while (bitIter != 0x80);
    

    通过使用do ... while 循环,它将终止问题,并且您可以在第一次迭代之前摆脱不必要的循环测试比较(除非您的编译器已经对其进行了优化)。

    您可以尝试手动展开循环,八次,每一位一次。

    【讨论】:

    • 确实——如果bitIterunsigned charchar 是8 位,那么我很惊讶循环会退出。永远不会完成可能会让某人认为它很慢,但肯定会有其他事情发生(uchar 不是 8 位?,发布的代码不是真正使用的代码?)。显然,如果循环从未完成,他们会寻找的不仅仅是优化。
    【解决方案4】:

    我假设当您进入此函数时 PORTC 处于已知状态:即数据和时钟线为 0? (或者时钟低数据高?)

    如果该假设成立,您甚至应该能够通过首先设置 value = ~(*byteIter); 然后执行 8 次来避免 @6502 的答案中的条件:

     PORTC|=BIT0;PORTC|=(value<<7)&BIT7;PORTC&=~(BIT7|BIT0);value>>=1;
    

    -或者,如果 Bit7 开始为高 -

     PORTC|=(BIT7|BIT0);PORTC&=(~BIT7|(value<<7));PORTC&=~BIT0;value>>=1;
    

    这里的优点是它避免了条件语句 - 这可能会严重破坏流水线处理器的速度。

    【讨论】:

      猜你喜欢
      • 2017-11-21
      • 1970-01-01
      • 2014-06-04
      • 1970-01-01
      • 2010-12-02
      • 2017-02-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多