打印

最近一点体会,优化要从大处着眼,小处着手

[复制链接]
4685|27
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
做MP3播放器嵌入式软件优化过程中,虽挖使尽浑身解数,播放320Kbps的音乐文件时,遇到32等小容量的SD卡时,仍断断续续,而插上2G的卡,能流畅播放,左思右想,发现小容量卡的瓶颈在每簇的扇区数很少,为1个或几个扇区,放完一个扇区的内容,又要去查找FAT表,找到下一簇,所以数据量供不应求。
不兼容没法用,用户是上帝。急中生智,发现只要将当前播放文件簇链特性提取出来,每查一次FAT表,得到起始簇号和后续连续的簇数量,则在播放过程中,不用查FAT表,直接用簇链加1来完成,效率提高几十倍。
评分
参与人数 4威望 +13 收起 理由
xwj + 10
mbutterfly + 1
ejack + 1
bhsdlmj + 1

相关帖子

沙发
古道热肠|  楼主 | 2010-12-25 11:27 | 只看该作者
原来的簇链查找函数如下:

#if 0
//备注:本函数假定BytePerSector为512字节。
ulong  mLinkCluster(ulong ulCurrent_Cluster)
{
        unsigned long ulResult;
        unsigned int uiOffset;        //扇区内的偏移量
        unsigned long fatOffset;
        unsigned long sector;
        uchar data *ucTemp;
//        unsigned int offset;
       

        if(FAT_TYPE == c_FAT_Type_FAT12)
        {
//                uiResult = ++uiCurrent_Cluster;
                fatOffset = ulCurrent_Cluster+(ulCurrent_Cluster>>1);
//                sector =  BootSector + RsvdSecCnt + (fatOffset /512);
                sector =  FATStartSecotr + (fatOffset /512);
                uiOffset = fatOffset % 512;

                if(uiOffset == 511)
                {
                        ReadSector_SD(sector,Sector_Buf);
                        ulResult = ((unsigned long)Sector_Buf[511]);
                        ReadSector_SD(sector+1,Sector_Buf);
                        ulResult |= (((unsigned long)Sector_Buf[0])<<8)&0xffff;
                }
                else
                {
                        ReadSector_SD(sector,Sector_Buf);
                        ulResult = Sector_Buf[uiOffset] + (((unsigned long)Sector_Buf[uiOffset+1])<<8);
                }

                if (ulCurrent_Cluster & 0x0001)
                {
                        ulResult >>= 4;
                }
                else
                {
                        ulResult &= 0x0fff;
                }

               
        }
        else if(FAT_TYPE == c_FAT_Type_FAT16)
        {
//                ReadSector_SD(  BootSector + RsvdSecCnt + uiCurrent_Cluster / 256, Sector_Buf );  /* 读取簇号所在的FAT扇区 */
//                ReadSector_SD( FATStartSecotr + uiCurrent_Cluster / 256, Sector_Buf );  /* 读取簇号所在的FAT扇区 */
                mReadSector(FATStartSecotr + ulCurrent_Cluster / 256);

                uiOffset = ( ulCurrent_Cluster + ulCurrent_Cluster ) & 0x01FF;

                ulResult = Sector_Buf[uiOffset]+  ( (ulong)Sector_Buf[uiOffset+1]<<8);
        }
        else if(FAT_TYPE == c_FAT_Type_FAT32)
        {
                mReadSector(FATStartSecotr + ulCurrent_Cluster / 128);

                uiOffset = ( ulCurrent_Cluster << 2) & 0x01FF;
//                uiOffset = ( ulCurrent_Cluster * 4) & 0x01FF;
                ucTemp = (uchar *)&ulResult;
                *ucTemp++ = Sector_Buf[uiOffset+3];
                *ucTemp++ = Sector_Buf[uiOffset+2];
                *ucTemp++ = Sector_Buf[uiOffset+1];
                *ucTemp++ = Sector_Buf[uiOffset];

//                ulResult = Sector_Buf[uiOffset]+  ( (ulong)Sector_Buf[uiOffset+1]<<8)+ ( (ulong)Sector_Buf[uiOffset+2]<<16)+ ( (ulong)Sector_Buf[uiOffset+3]<<24);

        }
        else
        {
               
        }

        return(ulResult);
}
#endif

使用特权

评论回复
板凳
古道热肠|  楼主 | 2010-12-25 11:28 | 只看该作者
优化后的簇链查找函数:
#if 1
//直接在FAT表中查找链接簇
ulong  mLinkCluster(ulong ulCurrent_Cluster)
{
        unsigned long ulResult;
        unsigned int uiOffset;        //扇区内的偏移量
        unsigned long fatOffset;
        unsigned long sector;
        uchar data *ucTemp;
//        unsigned int offset;
       
        if(ucFastGetClusterCount>1)
        {
                ucFastGetClusterCount--;
                return(ulCurrent_Cluster+1);
        }


        if(FAT_TYPE == c_FAT_Type_FAT12)
        {
//                uiResult = ++uiCurrent_Cluster;
                fatOffset = ulCurrent_Cluster+(ulCurrent_Cluster>>1);
//                sector =  BootSector + RsvdSecCnt + (fatOffset /512);
                sector =  FATStartSecotr + (fatOffset /512);
                uiOffset = fatOffset % 512;

                if(uiOffset == 511)
                {
                        ReadSector_SD(sector,Sector_Buf);
                        ulResult = ((unsigned long)Sector_Buf[511]);
                        ReadSector_SD(sector+1,Sector_Buf);
                        ulResult |= (((unsigned long)Sector_Buf[0])<<8)&0xffff;
                }
                else
                {
                        ReadSector_SD(sector,Sector_Buf);
                        ulResult = Sector_Buf[uiOffset] + (((unsigned long)Sector_Buf[uiOffset+1])<<8);
                }

                if (ulCurrent_Cluster & 0x0001)
                {
                        ulResult >>= 4;
                }
                else
                {
                        ulResult &= 0x0fff;
                }

               
        }
        else if(FAT_TYPE == c_FAT_Type_FAT16)
        {
//                ReadSector_SD(  BootSector + RsvdSecCnt + uiCurrent_Cluster / 256, Sector_Buf );  /* 读取簇号所在的FAT扇区 */
//                ReadSector_SD( FATStartSecotr + uiCurrent_Cluster / 256, Sector_Buf );  /* 读取簇号所在的FAT扇区 */
                mReadSector(FATStartSecotr + ulCurrent_Cluster / 256);

                uiOffset = ( ulCurrent_Cluster + ulCurrent_Cluster ) & 0x01FF;

                ulResult = Sector_Buf[uiOffset]+  ( (ulong)Sector_Buf[uiOffset+1]<<8);
               
                //计算当前FAT扇区内容连接连续簇号的数量
                {
                //        uint data uiTempOffset;
                //        uint idata uiTempCurrentCluster;
                //        uint idata uiTempResult;
                        #define uiTempOffset uiOffset
                        #define uiTempCurrentCluster fatOffset
                        #define uiTempResult  sector

                        ucFastGetClusterCount = 1;
                        uiTempOffset = uiOffset;
                        uiTempCurrentCluster = ulResult;
                        while(uiTempOffset<510)
                        {
                                uiTempOffset += 2;        //指向下一个单元                                       
                                uiTempResult = Sector_Buf[uiTempOffset]+  ( (uint)Sector_Buf[uiTempOffset+1]<<8);
                                if(uiTempResult > 0xFFF8)
                                {
                                        break;
                                }
                                if(uiTempResult != (uiTempCurrentCluster+1))
                                {
                                        break;
                                }
                                else
                                {
                                        ucFastGetClusterCount++;
                                        uiTempCurrentCluster = uiTempResult; //查找下一个做准备
                                }
                        }
                }
        }
        else if(FAT_TYPE == c_FAT_Type_FAT32)
        {
                mReadSector(FATStartSecotr + ulCurrent_Cluster / 128);

                uiOffset = ( ulCurrent_Cluster << 2) & 0x01FF;
//                uiOffset = ( ulCurrent_Cluster * 4) & 0x01FF;
                ucTemp = (uchar *)&ulResult;
                *ucTemp++ = Sector_Buf[uiOffset+3];
                *ucTemp++ = Sector_Buf[uiOffset+2];
                *ucTemp++ = Sector_Buf[uiOffset+1];
                *ucTemp++ = Sector_Buf[uiOffset];

//                ulResult = Sector_Buf[uiOffset]+  ( (ulong)Sector_Buf[uiOffset+1]<<8)+ ( (ulong)Sector_Buf[uiOffset+2]<<16)+ ( (ulong)Sector_Buf[uiOffset+3]<<24);
                //计算当前FAT扇区内容连接连续簇号的数量
                {
//                        uint data uiTempOffset;
//                        ulong idata ulTempCurrentCluster;
//                        ulong idata ulTempResult;
                        #define uiTempOffset uiOffset
                        #define ulTempCurrentCluster fatOffset
                        #define ulTempResult  sector


                        ucFastGetClusterCount = 1;
                        uiTempOffset = uiOffset;
                        ulTempCurrentCluster = ulResult;
                        while(uiTempOffset<508)
                        {
                                uiTempOffset += 4;        //指向下一个单元                                       
                                ucTemp = (uchar *)&ulTempResult;
                                *ucTemp++ = Sector_Buf[uiTempOffset+3];
                                *ucTemp++ = Sector_Buf[uiTempOffset+2];
                                *ucTemp++ = Sector_Buf[uiTempOffset+1];
                                *ucTemp++ = Sector_Buf[uiTempOffset];

                                if(ulTempResult > 0xFFFFFFF8)
                                {
                                        break;
                                }
                                if(ulTempResult != (ulTempCurrentCluster+1))
                                {
                                        break;
                                }
                                else
                                {
                                        ucFastGetClusterCount++;
                                        ulTempCurrentCluster = ulTempResult; //查找下一个做准备
                                }
                        }
                }


        }
        else
        {
               
        }

        return(ulResult);
}
#endif

使用特权

评论回复
地板
古道热肠|  楼主 | 2010-12-25 11:31 | 只看该作者
以下定义是很有趣的事:
         //        uint data uiTempOffset;
                //        uint idata uiTempCurrentCluster;
                //        uint idata uiTempResult;
                        #define uiTempOffset uiOffset
                        #define uiTempCurrentCluster fatOffset
                        #define uiTempResult  sector

因为芯片RAM不够,原来仿真通过的功能函数在目标板上不能用,为了保持程序的可读性,找出本函数中的空闲变量,用伪定义来起个别命,改动的地方小。

使用特权

评论回复
5
xwj| | 2010-12-25 11:31 | 只看该作者
先顶后看

使用特权

评论回复
6
古道热肠|  楼主 | 2010-12-25 11:34 | 只看该作者
小处着眼之笔,在函数中得到反映,摘抄如下:
             ucTemp = (uchar *)&ulResult;
                *ucTemp++ = Sector_Buf[uiOffset+3];
                *ucTemp++ = Sector_Buf[uiOffset+2];
                *ucTemp++ = Sector_Buf[uiOffset+1];
                *ucTemp++ = Sector_Buf[uiOffset];

//                ulResult = Sector_Buf[uiOffset]+  ( (ulong)Sector_Buf[uiOffset+1]<<8)+ ( (ulong)Sector_Buf[uiOffset+2]<<16)+ ( (ulong)Sector_Buf[uiOffset+3]<<24);

使用特权

评论回复
7
bhsdlmj| | 2010-12-25 11:41 | 只看该作者
可以考虑 在数组里面套结构体  我是菜鸟多谢

使用特权

评论回复
8
zdc393589060| | 2010-12-25 13:05 | 只看该作者
看看!!

使用特权

评论回复
9
SLEET1986| | 2010-12-25 14:56 | 只看该作者
just read

使用特权

评论回复
10
zzz1367| | 2010-12-25 21:47 | 只看该作者
mark

使用特权

评论回复
11
123jj| | 2010-12-26 04:17 | 只看该作者
顶,热肠的好贴都得顶!

使用特权

评论回复
12
ejack| | 2010-12-26 08:40 | 只看该作者
左思右想,发现小容量卡的瓶颈在每簇的扇区数很少

这是花去80%时间和汗水的地方啊,感谢LZ分享血汗

洗碗机的签名链接真好……

使用特权

评论回复
13
mbutterfly| | 2010-12-26 09:40 | 只看该作者
收藏备用。

使用特权

评论回复
14
mbutterfly| | 2010-12-26 09:41 | 只看该作者
XWJ很吝啬,从来不送分!:)

使用特权

评论回复
15
nongfuxu| | 2010-12-26 11:29 | 只看该作者
好贴,学习了!

使用特权

评论回复
16
古道热肠|  楼主 | 2010-12-26 12:23 | 只看该作者
谢谢大家评分,文件系统的FAT缓冲区和数据扇冲区是应该分开设置的,只是单片机通常内存紧张,2个缓冲区就吃掉1K的RAM,在俺的板子上根本不可能这么宽裕的。

使用特权

评论回复
17
古道热肠|  楼主 | 2010-12-26 13:31 | 只看该作者
再来一例说明,优化的方式方法是多种多样的,每种优化的用途适用于特定的块合,MP3模块支持读取SD卡中某个文件的内容,以扇区方式访问,如果以普通方式查找文件的某个位置所在的扇区号,需要根据FAT表查找簇链,所以也存在多次读的问题,由于读取文件内容是一个完整的事件,不会存在主贴中扇区交叉读取的现象。我们采用了扇区读缓冲技术,查找文件内容相当的快。

uint32 idata LastFatLba=0xFFFFFFFF;
//备注:本函数假定BytePerSector为512字节。
uint  FastLinkCluster(uint ulCurrent_Cluster)
{
unsigned int ulResult;
unsigned int uiOffset; //扇区内的偏移量
unsigned int fatOffset;
unsigned long sector;
// unsigned int offset;

if(FAT_TYPE == c_FAT_Type_FAT12)
{
//  uiResult = ++uiCurrent_Cluster;
  fatOffset = ulCurrent_Cluster+(ulCurrent_Cluster>>1);
//  sector =  BootSector + RsvdSecCnt + (fatOffset /512);
  sector =  FATStartSecotr + (fatOffset /512);
  uiOffset = fatOffset % 512;
  if(uiOffset == 511)
  {
   ReadSector_SD(sector,Sector_Buf);
   ulResult = (Sector_Buf[511]);
   ReadSector_SD(sector+1,Sector_Buf);
   ulResult |= ((Sector_Buf[0])<<8)&0xffff;
  }
  else
  {
   if(sector != LastFatLba)
   {
    LastFatLba = sector;
    ReadSector_SD(sector,Sector_Buf);
   }
   ulResult = Sector_Buf[uiOffset] + ((uint)(Sector_Buf[uiOffset+1])<<8);
  }
  if (ulCurrent_Cluster & 0x0001)
  {
   ulResult >>= 4;
  }
  else
  {
   ulResult &= 0x0fff;
  }
  
}
else if(FAT_TYPE == c_FAT_Type_FAT16)
{
//  ReadSector_SD(  BootSector + RsvdSecCnt + uiCurrent_Cluster / 256, Sector_Buf );  /* 读取簇号所在的FAT扇区 */
//  ReadSector_SD( FATStartSecotr + uiCurrent_Cluster / 256, Sector_Buf );  /* 读取簇号所在的FAT扇区 */
  sector = FATStartSecotr + ulCurrent_Cluster / 256;
  if(sector != LastFatLba)
  {
   LastFatLba = sector;
   mReadSector(sector);
  }
  uiOffset = ( ulCurrent_Cluster + ulCurrent_Cluster ) & 0x01FF;
  ulResult = Sector_Buf[uiOffset]+  ( (uint)Sector_Buf[uiOffset+1]<<8);
}
else
{
  
}
return(ulResult);
}

呵呵,函数名都是起的"快速查找链接"

该函数初始化“脏标志”后,可以透明使用。

使用特权

评论回复
18
古道热肠|  楼主 | 2010-12-26 13:34 | 只看该作者
核心部分为:
  if(sector != LastFatLba)
   {
    LastFatLba = sector;
    ReadSector_SD(sector,Sector_Buf);
   }
通过比较当前要访问的扇区号与上一次访问的扇区号是否相同来做不同的处理,如果相同,则不用读取物理SD卡。
如果出现不同的现象,则从物理介质中读取内容,并更新最新扇区号标志位。

使用特权

评论回复
19
古道热肠|  楼主 | 2010-12-26 13:35 | 只看该作者
此函数的主调函数如下:
//读取文件中的某个扇区的内容到Buff
uchar ReadFileSector(ulong ulSectorNumber)
{
        uchar Status;

        uiCurrentFileClusterNo = mGetPointWord( (unsigned char *)&FDB_Current.DIR_FstClusLO );  /* 获取文件的首簇号*/
//        uiCurrentFileClusterNo = uiHZKFileStartCluster;
        ulSectorAddress = mClusterToLba( uiCurrentFileClusterNo );
        ucSectorOfClusterCount= SecPerClus;                        //簇内扇区总数

        if(ulSectorNumber == 0)
        {
                Status = mReadSector(ulSectorAddress++);  /* 读取首簇到缓冲区 */
        }
        else
        {

                LastFatLba=0xFFFFFFFF;
                while(ulSectorNumber--)
                {
                        if((--ucSectorOfClusterCount) ==0)
                        {
                                uiCurrentFileClusterNo = FastLinkCluster( uiCurrentFileClusterNo );  /* 获取链接簇,返回0说明错误 */
        //                        uiCurrentFileClusterNo++;
                                if(FAT_TYPE == c_FAT_Type_FAT12)
                                {
                                        if(uiCurrentFileClusterNo < 0xFF8)
                                        {
                                                ucSectorOfClusterCount= SecPerClus;
                                                ulSectorAddress = mClusterToLba( uiCurrentFileClusterNo );
                                        }
                                        else
                                        {
                                                return(ERR_NOFIND);
                                        }
                                }
                                else if(FAT_TYPE == c_FAT_Type_FAT16)
                                {
                                        if(uiCurrentFileClusterNo < 0xFFF8)
                                        {
                                                ucSectorOfClusterCount= SecPerClus;
                                                ulSectorAddress = mClusterToLba( uiCurrentFileClusterNo );
                                        }
                                        else
                                        {
                                                return(ERR_NOFIND);
                                        }
                                }
                        }
                        ulSectorAddress++;
                }//查找扇区号结束

                Status = mReadSector(ulSectorAddress-1);  /* 读取首簇到缓冲区 */
        }
        return(Status);
}

使用特权

评论回复
20
程序匠人| | 2011-1-10 22:09 | 只看该作者
我决定推荐这篇帖子参加“裤子博览会”。呵呵

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:以VS1003B和山景SOC芯片为背景,倾心研制数字化语音录放产品. 排忧邮箱:xg_2004_sy@126.com 得意之作是做了个AVR高压编程器,用起来爽歪歪, 串口MP3录放音模块,全面进入数字录放音时代

284

主题

6411

帖子

16

粉丝