gpt4 book ai didi

c - 为什么这个嵌套的 for 循环比展开相同的代码慢得多?

转载 作者:太空宇宙 更新时间:2023-11-04 01:47:55 25 4
gpt4 key购买 nike

我正在使用 ATtiny85 和 128x64px OLED 构建一个小型玩具控制台。在我的初始构建中,我使用内置的 shiftOut()digitalWrite() 函数将显示数据移出到屏幕 Controller 。

这使我获得了 ~5fps,这有点令人失望。

我编写了自己的函数,使用直接端口操作来发送数据,速度得到了显着提高~23fps,这还不错。这是该函数:

void shift_out_block(block)
{
byte b;
for (byte i = 0; i < 8; i++)
{
b = pgm_read_byte(block+i);

for (byte j=0 ; j < 8 ; j++)
{
if ( !!( b & (1 << j)) )
{
PORTB |= 1 << SDA;
}
else
{
PORTB &= ~(1 << SDA);
}

PORTB |= 1 << SCL; // HIGH
PORTB &= ~(1 << SCL); // LOW
}
}
}

23fps 没问题,但它不是 30 甚至 60fps(如果它是 24fps,我实际上会把它留在这里,但奇数......)。

我理解为什么删除库调用和直接操作端口可以使事情有如此大的改善 - 这些库是为在各种不同的 MCU 上工作而编写的。

我依稀记得解开循环是一个东西,所以我解开内部的 for 循环:

void shift_out_block()
{
byte b;
for (byte i = 0; i < 8; i++)
{
b = pgm_read_byte(block+i);

if ( !!( b & (1 << 0)) )
{
PORTB |= 1 << SDA;
}
else
{
PORTB &= ~(1 << SDA);
}

PORTB |= 1 << SCL; // HIGH
PORTB &= ~(1 << SCL); // LOW

if ( !!( b & (1 << 1)) )
{
PORTB |= 1 << SDA;
}
else
{
PORTB &= ~(1 << SDA);
}

PORTB |= 1 << SCL; // HIGH
PORTB &= ~(1 << SCL); // LOW

if ( !!( b & (1 << 2)) )
{
PORTB |= 1 << SDA;
}
else
{
PORTB &= ~(1 << SDA);
}

PORTB |= 1 << SCL; // HIGH
PORTB &= ~(1 << SCL); // LOW

if ( !!( b & (1 << 3)) )
{
PORTB |= 1 << SDA;
}
else
{
PORTB &= ~(1 << SDA);
}

PORTB |= 1 << SCL; // HIGH
PORTB &= ~(1 << SCL); // LOW

if ( !!( b & (1 << 4)) )
{
PORTB |= 1 << SDA;
}
else
{
PORTB &= ~(1 << SDA);
}

PORTB |= 1 << SCL; // HIGH
PORTB &= ~(1 << SCL); // LOW

if ( !!( b & (1 << 5)) )
{
PORTB |= 1 << SDA;
}
else
{
PORTB &= ~(1 << SDA);
}

PORTB |= 1 << SCL; // HIGH
PORTB &= ~(1 << SCL); // LOW

if ( !!( b & (1 << 6)) )
{
PORTB |= 1 << SDA;
}
else
{
PORTB &= ~(1 << SDA);
}

PORTB |= 1 << SCL; // HIGH
PORTB &= ~(1 << SCL); // LOW

if ( !!( b & (1 << 7)) )
{
PORTB |= 1 << SDA;
}
else
{
PORTB &= ~(1 << SDA);
}

PORTB |= 1 << SCL; // HIGH
PORTB &= ~(1 << SCL); // LOW
}
}

毫不费力,复制粘贴 7 次。给我将近 75fps - 原始函数执行时间约为 42 毫秒,新的丑陋函数仅需 ~13 毫秒。

出于兴趣,我将发送位部分分解为一个单独的函数并调用了 8 次:

void shift_out_bit(bool bit)
{
if ( bit )
{
PORTB |= 1 << SDA;
}
else
{
PORTB &= ~(1 << SDA);
}

PORTB |= 1 << SCL; // HIGH
PORTB &= ~(1 << SCL); // LOW
}

void shift_out_block()
{
byte b;
for (byte i = 0; i < 8; i++)
{
b = pgm_read_byte(block+i);

shift_out_bit( !!( b & (1 << 0)) );
shift_out_bit( !!( b & (1 << 1)) );
shift_out_bit( !!( b & (1 << 2)) );
shift_out_bit( !!( b & (1 << 3)) );
shift_out_bit( !!( b & (1 << 4)) );
shift_out_bit( !!( b & (1 << 5)) );
shift_out_bit( !!( b & (1 << 6)) );
shift_out_bit( !!( b & (1 << 7)) );
}
}

~22 毫秒执行,或 45.4545454545 fps,这甚至不是一个很好的数字。

无论如何我都不是 C 程序员 - Python 是我经常出没的地方(我最初是用 Python/RPi 开始这个项目的,但非常很快就放弃了!) .

为什么这样一个核心语言特性在这种情况下会慢很多?随着我的项目变得越来越复杂,我还应该考虑哪些其他优化?

最佳答案

考虑在最内层循环内完成的“有效负载”操作:

  • 检查 b 中的特定位
  • 条件跳转处理PORTB |= 1 << SDAPORTB &= ~(1 << SDA)
  • PORTB 的三个操作

这就是循环的所有展开版本;不需要做任何其他事情,甚至不需要移动 1向左j次,因为编译器计算常量表达式,并且 1 << 4变得简单16 .

另一方面,没有展开的循环必须做额外的事情来保持循环:

  • 递增j
  • 比较 j到 8
  • 转移 1离开 j职位
  • 无条件跳转到循环开头

当循环展开时,CPU 不再负担这些“非有效负载”指令,因此执行速度上升。

Why is such a core language feature so much slower in this situation?

许多现代编译器会根据优化设置自动为您展开循环。

关于c - 为什么这个嵌套的 for 循环比展开相同的代码慢得多?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49832116/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com