打印

深入浅出: C 与 C++ 中“结构直接复制”的探讨

[复制链接]
楼主: highgear
手机看帖
扫描二维码
随时随地手机跟帖
41
我呢是比较菜,但是我相信Keil自带的函数肯定是有他的道理,他们也不是白痴
开始我写函数的时候,看别人的函数都是太复杂了,偷偷得精简
不过到后来,考虑移植和通用,慢慢的写的和人家的就差不多了
说错话,做错事不要紧,别人指正时只要虚心接受,有则改之无则加勉就可以了
我才没事找人开打,不过那儿人都一样,只要保持住自己的心境,淡定淡定,O(∩_∩)O哈哈~

使用特权

评论回复
评分
参与人数 1威望 +1 收起 理由
highgear + 1
42
刘前辈| | 2011-5-12 16:04 | 只看该作者
这下可以看清第二个文件夹 test 里的内容了。上面的内容主要是自己的隐私文件名,不愿公开而已。

使用特权

评论回复
43
aihe| | 2011-5-12 19:12 | 只看该作者
本帖最后由 aihe 于 2011-5-12 19:14 编辑

既然你这么愿意帮忙,那就帮我把前几个月写的一段程序再优化优化,注芯片是STM8

void EE_Wrait(unsigned int pSrc, unsigned int pDst,unsigned char ucLength)      //写数据内置存储器
{        //从源地址写入到----目的地址----长度
    unsigned char ucCount;
    unsigned char *pDstAddr = (unsigned char *)pDst;
    unsigned char *pSrcAddr = (unsigned char *)pSrc;
   // 对数据EEPROM进行解锁
   do
   {
     FLASH_DUKR = 0xae;                    // 写入第二个密钥
     FLASH_DUKR = 0x56;                    // 写入第一个密钥
   }
   while((FLASH_IAPSR & 0x08) == 0);      // 若解锁未成功,则重新再来
   
   for(ucCount=0;ucCount<ucLength;ucCount++)
   {
     *pDstAddr = *pSrcAddr;
     while((FLASH_IAPSR & 0x04) == 0);        // 等待写操作成功
     pSrcAddr ++;
     pDstAddr ++;
    }
}

使用特权

评论回复
评分
参与人数 1威望 +1 收起 理由
highgear + 1
44
highgear|  楼主 | 2011-5-12 21:04 | 只看该作者
刘工,这就对了,你若技术讨论,那么就继续技术讨论;若继续污言秽语恶意中伤,那highgear就奉陪,继续问候你的家人。

刘工,优化 C?COPY 没有意义,无论再如何优化,keil c 下 a1 = a2 的效率都是很低。一个简单的一字节结构,仍然使用大量代码,优化 c?copy 失去了意义。而且,我一直强调, keil c 没有DMA 或 block Move 指令,因此,memcpy 的效率也不会比手工 c 代码高。

不必纠缠在 C?COPY 的优化上,此举对 a1 = a2 帮助不大。

使用特权

评论回复
45
highgear|  楼主 | 2011-5-12 21:30 | 只看该作者
a1 = a2 在 keil c 下效率低下,而 Visual C++ 生成的代码却效率极高.

一字节结构:
a1 = a2;
004113B2  mov         al,byte ptr [a2]
004113B5  mov         byte ptr [a1],al


3 字节结构:

        a1 = a2;
004113B2  mov         ax,word ptr [a2]
004113B6  mov         word ptr [a1],ax
004113BA  mov         cl,byte ptr [ebp-12h]
004113BD  mov         byte ptr [ebp-6],cl


8 字节结构:
        a1 = a2;
004113BC  mov         eax,dword ptr [ebp-20h]
004113BF  mov         dword ptr [ebp-10h],eax
004113C2  mov         ecx,dword ptr [ebp-1Ch]
004113C5  mov         dword ptr [ebp-0Ch],ecx


12 字节结构:
        a1 = a2;
004113BC  mov         eax,dword ptr [ebp-28h]
004113BF  mov         dword ptr [ebp-14h],eax
004113C2  mov         ecx,dword ptr [ebp-24h]
004113C5  mov         dword ptr [ebp-10h],ecx
004113C8  mov         edx,dword ptr [ebp-20h]
004113CB  mov         dword ptr [ebp-0Ch],edx


直至25字节以上,vc++ 才开始使用block move, 并未像 keil c 那样调用 memcpy.
a1 = a2;
0041354C  mov         ecx,6
00413551  lea         esi,[ebp-48h]
00413554  lea         edi,[ebp-24h]
00413557  rep movs    dword ptr es:[edi],dword ptr [esi]
00413559  movs        byte ptr es:[edi],byte ptr [esi]

通过比较,可以这么猜测:结构复制在 c 不常用的原因,导致 keil c 并未对其优化;而 vc++ 中则因为结构复制的大量使用(c++中,结构是all public 的类),则特别优化,手工生成的代码也不过如此了。

使用特权

评论回复
46
highgear|  楼主 | 2011-5-12 22:01 | 只看该作者
本帖最后由 highgear 于 2011-5-12 22:08 编辑

如果对 c?copy 优化感兴趣,可以看看 keil c 论坛的帖子(我前面摘抄的英文源自于此):

http://www.keil.com/forum/6950/
http://www.keil.com/forum/8701/

Custom ?C?COPY ?
Bill Webster
I would dearly love to replace some of the Keil library functions that are used by intrinsics, such as ?C?COPY, which is called by memcpy (and by other things). My current goal is to generate runtime errors on certain types of copies, but replacement would be useful for other purposes.

I've actually written my own implementation of ?C?COPY, which works fine. The inline instructions are still emitted by the compiler and my version is linked.

However, I'm concerned about compatibility with future compiler releases.

I'm supposing that the interface can't change, because this would break older libraries, which could be third party.

But other library functionality may depend on something internal to the Keil ?C?COPY implementation, or may do so in future.

For my current purposes, I'm thinking of patching the entry to the Keil ?C?COPY after the code is built, to call my checking routine. This only requires that the interface doesn't change.

Has anyone else tried anything like this? Any comments on my assumption that the interface can't change?

PS - I know I can acheive my ends by writing my own memcpy and redefining memcpy() with a macro, but then heaps more code will be generated, because the calls will be far less efficient, without the special parameter passing.

使用特权

评论回复
47
刘前辈| | 2011-5-13 09:22 | 只看该作者
43楼aihe 的帮忙工作有没有效益?若是一般研究,我就交给学生做了。
小意思,可以到我家里来共同探讨。我请你吃饭。
     我住静安。

使用特权

评论回复
48
ssy250| | 2011-5-13 09:37 | 只看该作者
我等,程序盲,基本不会考虑效率问题,最直接的方法是升级处理器~~

使用特权

评论回复
评分
参与人数 1威望 +1 收起 理由
刘前辈 + 1 没错,根本无所谓。不需要那么快,到后来处 ...
49
刘前辈| | 2011-5-13 10:49 | 只看该作者
本帖最后由 刘前辈 于 2011-5-13 11:41 编辑

2个相同算法之间相差几十个字节长度,根本无所谓效率问题,又不是核反应堆。
而且多数情况下,code 长度大(实际开销小)的那个算法更通用完善。

像LZ 指责keil C51 代码效率低,又臭又长,纯粹是无知。微软的Windows 臭不臭,长不长?LZ 优化一个给盖茨 看看?到微软应聘去吧。在这混,太屈才了。



、、

使用特权

评论回复
50
刘前辈| | 2011-5-13 11:32 | 只看该作者
本帖最后由 刘前辈 于 2011-5-13 11:44 编辑

43楼 aihe:

学生交卷了,换了一种算法,code 缩减了一半多。——功能完全一样。
请你检验一下,有什么错误没有。如果正确,是不是应该表示一下?错了请指正。(我说他写的不错,如果证实,还得再次点名欣赏,可以就此强调一下概念。)

无论正/ 误,都可以让网友学习一下。

修改代码如下:
void EE_Wrait(unsigned int  pSrc,  unsigned int  pDst,  unsigned char  ucLength)   
{  
   unsigned char ucCount;
   unsigned char  idata * pDstAddr =&pDst;
   unsigned char  idata * pSrcAddr =&pSrc;
  
   do
   {
     FLASH_DUKR = 0xae;                 
     FLASH_DUKR = 0x56;  
   }

   while((FLASH_IAPSR & 0x08) == 0);   
   
  for(ucCount=0; ucCount < ucLength; ucCount++)
  {
           pDstAddr [ucCount] = pSrcAddr [ucCount] ;

                   while((FLASH_IAPSR & 0x04) == 0);      
     //pSrcAddr ++;
     //pDstAddr ++;
    }
}
code= 52 ;   // 还要优化么?

原code=139 ;


//

使用特权

评论回复
51
123jj| | 2011-5-13 12:11 | 只看该作者
43楼 aihe老师的程序已是最优化的了,赞一个~~~

aihe老师注明芯片是STM8,真搞不懂,LS的大师怎么把51的 idata 存储器用于STM8? STM8芯片有 idata 存储器吗? :L

使用特权

评论回复
52
无名蚂蚁| | 2011-5-13 13:18 | 只看该作者
学习了

使用特权

评论回复
53
英雄无敌六| | 2011-5-13 13:21 | 只看该作者
43楼 aihe:

学生交卷了,换了一种算法,code 缩减了一半多。——功能完全一样。
请你检验一下,有什么错误没有。如果正确,是不是应该表示一下?错了请指正。(我说他写的不错,如果证实,还得再次点名欣赏,可以就 ...
刘前辈 发表于 2011-5-13 11:32

void EE_Wrait(unsigned int  pSrc,  unsigned int  pDst,  unsigned char  ucLength)   
{  
   unsigned char ucCount;
   unsigned char  idata * pDstAddr =&pDst;
   unsigned char  idata * pSrcAddr =&pSrc;


pDstAddr =&pDst根本就是错误的,怎么改的?

使用特权

评论回复
54
123jj| | 2011-5-13 14:46 | 只看该作者
LS小六子,大师哪会错?

错的肯定不是小三就是小六~~~  :lol

使用特权

评论回复
55
aihe| | 2011-5-13 16:38 | 只看该作者
把你的程序替代,编译通不过

#error cpstm8 main.c:43(31+4) incompatible pointer types

我是这样调用的
EE_Wrait((unsigned int)&Sys_Set[0].Tim2_F,(0x4000+(((unsigned int)&Sys_Set[0].Tim2_F)-(unsigned int)&Sys_Set[0])),sizeof(Sys_Set[0].Tim2_F));
或这样
EE_Wrait((unsigned int)&Start_Set,0x4000,sizeof(Start_Set));

但是我发现我的代码并不是很优化,比另外的写法多占有空间,但是这样写很直观

使用特权

评论回复
56
CC2530| | 2011-5-13 16:51 | 只看该作者
IAR STM8 1.2 直接使用__eeprom关键字访问EEPROM 

STM8L15X:
#include "stm8l15x_conf.h"

void __eeprom_write_8(unsigned short addr_eep,unsigned char data)
{
FLASH_WaitForLastOperation(FLASH_MemType_Data);
FLASH_Unlock(FLASH_MemType_Data);

FLASH_ProgramByte(addr_eep, data);

FLASH_WaitForLastOperation(FLASH_MemType_Data);
FLASH_Lock(FLASH_MemType_Data);
}

void __eeprom_write_16(unsigned short addr_eep,unsigned short data)
{
FLASH_WaitForLastOperation(FLASH_MemType_Data);
FLASH_Unlock(FLASH_MemType_Data);

FLASH_ProgramByte(addr_eep, data/256);
FLASH_WaitForLastOperation(FLASH_MemType_Data);

FLASH_ProgramByte(addr_eep+1, data%256);
FLASH_WaitForLastOperation(FLASH_MemType_Data);

FLASH_Lock(FLASH_MemType_Data);
}

void __eeprom_write_32(unsigned short addr_eep,unsigned long data)
{
FLASH_WaitForLastOperation(FLASH_MemType_Data);
FLASH_Unlock(FLASH_MemType_Data);

FLASH_ProgramByte(addr_eep, (unsigned char)(data>>24));
FLASH_WaitForLastOperation(FLASH_MemType_Data);

FLASH_ProgramByte(addr_eep+1, (unsigned char)(data>>16));
FLASH_WaitForLastOperation(FLASH_MemType_Data);

FLASH_ProgramByte(addr_eep+2, (unsigned char)(data>>8));
FLASH_WaitForLastOperation(FLASH_MemType_Data);

FLASH_ProgramByte(addr_eep+3, (unsigned char)(data>>0));
FLASH_WaitForLastOperation(FLASH_MemType_Data);

FLASH_Lock(FLASH_MemType_Data);
}


void __eeprom_write_many(unsigned short addr_eep,unsigned short size,unsigned short dummy,unsigned short addr_ram)
{

FLASH_WaitForLastOperation(FLASH_MemType_Data);
FLASH_Unlock(FLASH_MemType_Data);

for(unsigned short i=0;i<size;i++)
{
FLASH_ProgramByte(addr_eep+i, *((unsigned char *)(addr_ram)+i));
FLASH_WaitForLastOperation(FLASH_MemType_Data);
}


FLASH_Lock(FLASH_MemType_Data);

}



STM8S:
#include "stm8s_conf.h"

void __eeprom_write_8(unsigned short addr_eep,unsigned char data)
{
FLASH_WaitForLastOperation(FLASH_MEMTYPE_DATA);
FLASH_Unlock(FLASH_MEMTYPE_DATA);

FLASH_ProgramByte(addr_eep, data);

FLASH_WaitForLastOperation(FLASH_MEMTYPE_DATA);
FLASH_Lock(FLASH_MEMTYPE_DATA);
}

void __eeprom_write_16(unsigned short addr_eep,unsigned short data)
{
FLASH_WaitForLastOperation(FLASH_MEMTYPE_DATA);
FLASH_Unlock(FLASH_MEMTYPE_DATA);

FLASH_ProgramByte(addr_eep, data/256);
FLASH_WaitForLastOperation(FLASH_MEMTYPE_DATA);

FLASH_ProgramByte(addr_eep+1, data%256);
FLASH_WaitForLastOperation(FLASH_MEMTYPE_DATA);

FLASH_Lock(FLASH_MEMTYPE_DATA);
}

void __eeprom_write_32(unsigned short addr_eep,unsigned long data)
{
FLASH_WaitForLastOperation(FLASH_MEMTYPE_DATA);
FLASH_Unlock(FLASH_MEMTYPE_DATA);

FLASH_ProgramByte(addr_eep, (unsigned char)(data>>24));
FLASH_WaitForLastOperation(FLASH_MEMTYPE_DATA);

FLASH_ProgramByte(addr_eep+1, (unsigned char)(data>>16));
FLASH_WaitForLastOperation(FLASH_MEMTYPE_DATA);

FLASH_ProgramByte(addr_eep+2, (unsigned char)(data>>8));
FLASH_WaitForLastOperation(FLASH_MEMTYPE_DATA);

FLASH_ProgramByte(addr_eep+3, (unsigned char)(data>>0));
FLASH_WaitForLastOperation(FLASH_MEMTYPE_DATA);

FLASH_Lock(FLASH_MEMTYPE_DATA);
}


void __eeprom_write_many(unsigned short addr_eep,unsigned short size,unsigned short dummy,unsigned short addr_ram)
{

FLASH_WaitForLastOperation(FLASH_MEMTYPE_DATA);
FLASH_Unlock(FLASH_MEMTYPE_DATA);

for(unsigned short i=0;i<size;i++)
{
FLASH_ProgramByte(addr_eep+i, *((unsigned char *)(addr_ram)+i));
FLASH_WaitForLastOperation(FLASH_MEMTYPE_DATA);
}

FLASH_Lock(FLASH_MEMTYPE_DATA);
}


主程序测试:
volatile __eeprom __no_init char eep_u8;
volatile __eeprom __no_init short eep_u16;
volatile __eeprom __no_init long eep_u32;

void main( void )
{
eep_u8=123;
eep_u16=12345;
eep_u32=123456789;

while(1);

}

使用特权

评论回复
57
刘前辈| | 2011-5-13 17:21 | 只看该作者

我怎么闻到一股醋意

本帖最后由 刘前辈 于 2011-5-13 17:59 编辑

123jj先别太兴奋,不加idata一样。关键是躲过调用库函数。学生不过是移植到C51写的程序,连我也没玩过STM8。

小心最后自己打了自己的脸,让年轻学生弄个瞠目结舌不好看。尤其是女性。又是比他们年长20岁的女性。一个异型 MCU 你赢了他们二十几岁的学生很光彩是吧。“你们不懂吧……我就……”    呵呵,他们懂的东西,你这辈子入不了门。连皮毛都不可能懂。所长硬撑着都不懂,别说你了。

你还是专心注意力关心把自己的小车站起来为好。别老把眼睛盯着别人,跟所长似的,什么事非要和别人比个高下才兴奋?越比对手年龄越小了,现在和23岁的在校生对阵了?

学生说他再去看看STM8手册,我说不用,我一样给你优秀!研究生C语言你免考了。(你用不着把所有的MCU都去学会,你将来不是要打工的。你是做研究的,你以后的道路和别人不一样。)

有些人,干了一辈子,都在为别人打工。她什么类型的MCU都会一点,市场刷新太快,结果一生疲于奔命。其实我看这些自以为是的中年工程师,一辈子连51都学不好。会STM8?有用么?月薪最多10T而已。二踢脚,一生就这么过去了。姐姐还有发展前途?
         学生23岁,我看他比你43岁写的程序强多了。

        再告诉你和你尊重的所长,为什么51你们学一辈子也学不到头,看下面附件,……什么都知道点皮毛,然后就以为都会了。以为keil C效率低下?德国佬不过如此,缺少见识,看看德国佬干了什么,你技术生命不可能再学新技术了吧,现在知道再开始学也晚了……,这一生大概也只能玩玩小车了。

         我还欠你10分,给不出去。

    论坛上我就尊重2个人:殷文跃,XXX_xxx,他们看的懂我贴的是什么。他们知道Keil  C51的分量,从来不在这方面轻浮装懂。知道自己一生该干什么。



、、

使用特权

评论回复
58
123jj| | 2011-5-13 18:07 | 只看该作者
谈不上兴奋不兴奋,反正俺C语言不懂,那几十条基本指令都写不全。

无知者无畏,  俺早就不搞技术啦,怎么写程序?那是N年前的历史啦,已基本上忘个精光~~~ :lol

关于小车,前后匠人、老T叔撑着,后有 不光写程序小盆友跟着,哪里轮得上俺这个半吊子出手?

不过有一点刘小辈说对啦,俺的一生疲于奔命,养家糊口,买房养LP,还要供孩子留学。

一生就这么过去了。没有发展前途,彼此彼此,刘小辈也差不多如此吧,有能耐的人从不会把精力花在二姨家灌水!!! :lol

至于这一生也只能玩玩小车?这一点刘小辈也说对啦,俺小车也玩不转,要依靠匠人、老T叔、highgear、不光写程序等等,这些网友大力支持,没有这些网友的大力支持,俺一天也玩不下去。。。。。。。因为对这小玩意儿提不起兴趣。

听说刘小辈几年前就玩小车?何不拿出来晒一晒,让俺们菜鸟开开眼见~~~

使用特权

评论回复
59
刘前辈| | 2011-5-13 19:07 | 只看该作者
本帖最后由 刘前辈 于 2011-5-13 19:21 编辑

避开51data  写一个。学生的长辈是中医专家,总是教育后辈,中医界尤其反感什么事还没做到的时候就吹,“包治百病”“手到病除”“攻克癌症”……  (“移植”到论坛就是别人写程序还在调试阶段,就以为是最终结果了;就开始贬低他人不懂。认为别人很差劲……这也太着急了吧,高兴的太早了吧。……)中医界总是“我不能保证……容我试试……”。什么事给自己留后路。免得最后下不来台。——也写给所长,别**了几年 memcpy( ) ,实在下不来台,又换C++ 了。C++最后也不行,他还有C# ,还有JAVA。别到后来说胡话了。

#include<absacc.h>


void EE_Wrait(unsigned  int pSrc, unsigned  int pDst,unsigned char ucLength)      
{      
  do
   {
     FLASH_DUKR = 0xae;                  
     FLASH_DUKR = 0x56;                    
   }
   while((FLASH_IAPSR & 0x08) == 0);      

    while(ucLength--)
     {
     DBYTE[pSrc] = DBYTE[pDst];
    while((FLASH_IAPSR & 0x04) == 0);      
        pSrc++;
        pDst++;
     }
}
一样,code=51字节.

什么?STM8 没有absacc.h ?! ——学生自己做的。


、、

使用特权

评论回复
60
aihe| | 2011-5-13 19:10 | 只看该作者
淡定,淡定
技术以外的东西大家去同僚老乡版面去侃
刘xx的程序我放到Keil中试过了,优化效果的确明显
我先说我程序的缺点:
1、把++,改为——,改判零,会再少一个字节
2、开锁用完后没把锁再锁上,易招贼,(误写入)
刘xx你程序的问题在于:看我的函数名和注释就知道这是一个用于写EEROM的程序,无论哪个单片机EEROM都不能用IDATA存取,不符合题意。

使用特权

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

本版积分规则