一个1M的格式文本文件,每行存储时间和数值,之间用\t分开,行尾为0x0d0x0a
用CFile::Read读取,每次读8k
从中找出指定的行,遍历完所有行时间消耗:第一次:文件没被读过,不在系统磁盘缓冲区,耗时800毫秒
第二次到第n次,文件已在磁盘缓冲区,耗时8毫秒这说明99%的时间消耗在了磁盘IO上面。数据总要从磁盘读出来,内存映射方式肯定不行。关键是要提高IO性能,因为不能保证每个文件都在磁盘缓冲中。请教用哪个IO函数能提高磁盘IO性能呢?或者CFile打开方式是否对性能有影响,我是用二进制打开的。CFile::shareExclusive 方式也试过,对性能基本没影响。
用CFile::Read读取,每次读8k
从中找出指定的行,遍历完所有行时间消耗:第一次:文件没被读过,不在系统磁盘缓冲区,耗时800毫秒
第二次到第n次,文件已在磁盘缓冲区,耗时8毫秒这说明99%的时间消耗在了磁盘IO上面。数据总要从磁盘读出来,内存映射方式肯定不行。关键是要提高IO性能,因为不能保证每个文件都在磁盘缓冲中。请教用哪个IO函数能提高磁盘IO性能呢?或者CFile打开方式是否对性能有影响,我是用二进制打开的。CFile::shareExclusive 方式也试过,对性能基本没影响。
解决方案 »
- CTIME,赋值问题,请教
- 为什么创建的MFC ACTIVEX控件界面无法显示?
- 动态创建WMPlayer,并设置player->put_uiMode("none")时,为什么WMPlayer控件会有闪屏现象
- 解析http,ftp数据包时遇到的问题
- 量子遗传算法的应用研究,高手给点意见或建议!!
- B-S vs C-S
- 请问各位前辈,如何在一个文档类中打开一个BMP图片?谢谢各位了~!
- 现在做图形图像应该学点什么??
- wince下编程提示fatal error LNK1104: cannot open file 'ceddk.lib'
- 怕是没人会这个问题了!!!!!!!!!(VXD相关)
- 【在线求救】如何把ListControl里的内容导出到excel文档中!急。。急。。急。。。。。。。。。。。
- 关于用UDP 实现点对点聊天的问题
//lpszDate 日期格式yyyymmdd
//lpszStartTime 开始时间格式hhmmss
//pc 接收缓冲区
//nsize 缓冲大小
//返回字节数,含结尾的0
//功能,从开始时间起读一小时数据,当缓冲空间不够时可能小于一小时数据
int CTxtDB::GetRecFromFile(LPCTSTR lpszVarName,LPCTSTR lpszDate,LPCTSTR lpszStartTime,char *pc,int nsize)//从文件读取记录
{
CSafeLock lock(&m_Lock);//加锁 CString szFile=m_szPrjPath + "\\" + lpszDate + "\\" + lpszDate + "_" + lpszVarName + ".txd"; int ntime = atol(lpszStartTime);
int nh,nm,ns;
nh = ntime /10000;
nm = (ntime % 10000)/100;
ns = (ntime % 10000)%100; nh += 1;
if(nh > 23)
{
nh = 23;
nm = 59;
ns = 59;
} char stimee[16];
sprintf(stimee,"%02d%02d%02d",nh,nm,ns); CFile fl;
if(!fl.Open(szFile,CFile::modeRead))//文件不能打开,一般是不存在
return 0;//返回0字节
char s1[32],s2[32]; int s1p=0,s2p=0;
int i,k,n=fl.Read(m_sBolckBuf,8192);
int npos=0;
int np=0;
char c;
BOOL bStart = FALSE;
while ( n > 0)
{
for(i=0;i<n;i++)
{
c= m_sBolckBuf[i];
if(c=='\t')
npos=1;
else if((c==0x0d)||(c==0x0a))
{
npos =0;
s1[s1p] = 0;
s2[s2p] = 0; if((s1p>0)&&(s2p>0))
{
//处理一行
if(bStart == FALSE)
{
if(strcmp(s1,lpszStartTime) < 0)
{
s1p = 0;
s2p = 0;
continue;//没到起始点
}
bStart = TRUE;
}
if(strcmp(s1,stimee) >= 0)//超过一小时
{
*pc = 0;
fl.Close();
return np;
}
k=0;
while(s1[k] != 0)
{
*pc++ = s1[k];
np++;
k++;
}
*pc++ = '\t';
np++; k=0;
while(s2[k] != 0)
{
*pc++ = s2[k];
np++;
k++;
}
*pc++ = 0x0d;
np++; if(npos + 32 >= nsize)//超过缓冲大小
{
*pc=0;
fl.Close();
return np;
}
} s1p = 0;
s2p = 0;
}
else
{
if(npos == 0)
{
if(s1p < 30)
{
s1[s1p] = c;
s1p++;
}
}
else
{
if(s2p < 30)
{
s2[s2p] = c;
s2p++;
}
}
}
}//for
if( n < 8192)
break;
n=fl.Read(m_sBolckBuf,8192);
}//while
if(np>0)
*pc=0;
fl.Close();
return np;
}
3,4楼的方法没得效果,因为我做个实验的。
5,6楼说的有道理,从机械磁盘读取数据固定时间是无法减少的(无缓存条件下)。如果从系统层没得办法,考虑的解决方法主要是减小文件大小。减少文件大小的方式包括换存储格式、采用LZW压缩两种方法。
因此只能从文件大小上解决。
换文件格式,大小可减半,再用LZW压缩又可减少2/3,综合下来可减少文件从1M到200K左右,这样基本可提高5倍效率。
我在服务器写了一个中间层程序,提供TCP服务,客户需要查询读取指定文件中某时间段的数据。一次服务最多通过TCP传输64K数据。
以上测试时间实际包含了TCP传输时间,处理数据和TCP传输时间都很小,99%时间花在磁盘IO上了。为了提供更快速的数据服务和更多的同时连接客户,需要挖掘磁盘IO最大效率。简单的说就是服务方从指定磁盘文件检索客户需求的数据,最多一次传输64K数据,这个中间层程序要求尽量快。
下面是客户端测试代码CTimeCount ct;//计时
ct.start();
nret = hdc.OpenRec("L101.ic","20080724","230000");
ct.end();hdc.OpenRec函数返回时,已经将数据读到hdc对象的缓冲区了,以上代码客户从服务读取L101.ic 20080724 23点开始的1小时数据。服务器文件非缓冲时是800毫秒,缓冲时是8毫秒100:1的时间
{
public:
CTimeCount(){};
~CTimeCount(){};
void start()
{
QueryPerformanceFrequency(&litmp);
dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率
QueryPerformanceCounter(&litmp);
QPart1 = litmp.QuadPart;// 获得初始值
}; //开始计时
void end()
{
QueryPerformanceCounter(&litmp);
QPart2 = litmp.QuadPart;//获得中止值
dfMinus = (double)(QPart2-QPart1);
dfTim = dfMinus / dfFreq;// 获得对应的时间值,单位为秒
}; //停止计时 double time_milli(){
return dfTim * 1000.0;
}; //以毫秒精度输出所用时间 private:
LARGE_INTEGER litmp;
LONGLONG QPart1,QPart2;
double dfMinus, dfFreq, dfTim;
};
//lpszVarName 变量名
//lpszDate 日期格式yyyymmdd
//lpszStartTime 开始时间格式hhmmss
//pc 接收缓冲区
//nsize 缓冲大小
//返回字节数,含结尾的0
//功能,从开始时间起读一小时数据,当缓冲空间不够时可能小于一小时数据
int CTxtDB::GetRecFromFile(LPCTSTR lpszVarName,LPCTSTR lpszDate,LPCTSTR lpszStartTime,char *pc,int nsize)//从文件读取记录
{
CSafeLock lock(&m_Lock);//加锁 CString szFile=m_szPrjPath + "\\" + lpszDate + "\\" + lpszDate + "_" + lpszVarName + ".txd"; int ntime = atol(lpszStartTime);
int nh,nm,ns;
nh = ntime /10000;
nm = (ntime % 10000)/100;
ns = (ntime % 10000)%100; nh += 1;
if(nh > 23)
{
nh = 23;
nm = 59;
ns = 59;
} char stimee[16];
sprintf(stimee,"%02d%02d%02d",nh,nm,ns); CFile fl;
if(!fl.Open(szFile,CFile::modeRead))//文件不能打开,一般是不存在
return 0;//返回0字节
char s1[32],s2[32]; int s1p=0,s2p=0;
int i,k,n=fl.Read(m_sBolckBuf,8192);
int npos=0;
int np=0;
char c;
BOOL bStart = FALSE;
while ( n > 0)
{
for(i=0;i<n;i++)
{
c= m_sBolckBuf[i];
if(c=='\t')
npos=1;
else if((c==0x0d)||(c==0x0a))
{
npos =0;
s1[s1p] = 0;
s2[s2p] = 0; if((s1p>0)&&(s2p>0))
{
//处理一行
if(bStart == FALSE)
{
if(strcmp(s1,lpszStartTime) < 0)
{
s1p = 0;
s2p = 0;
continue;//没到起始点
}
bStart = TRUE;
}
if(strcmp(s1,stimee) >= 0)//超过一小时
{
*pc = 0;
fl.Close();
np++;
return np;
}
k=0;
while(s1[k] != 0)
{
*pc++ = s1[k];
np++;
k++;
}
*pc++ = '\t';
np++; k=0;
while(s2[k] != 0)
{
*pc++ = s2[k];
np++;
k++;
}
*pc++ = 0x0d;
np++; if(npos + 32 >= nsize)//超过缓冲大小
{
*pc=0;
fl.Close();
np++;
return np;
}
} s1p = 0;
s2p = 0;
}
else
{
if(npos == 0)
{
if(s1p < 30)
{
s1[s1p] = c;
s1p++;
}
}
else
{
if(s2p < 30)
{
s2[s2p] = c;
s2p++;
}
}
}
}//for
if( n < 8192)
break;
n=fl.Read(m_sBolckBuf,8192);
}//while
if(np>0)
*pc=0;
np++;
fl.Close();
return np;
}
unsigned1、锁不是锁文件的
2、读1M文件一次读完和每次都8K实际效果是一样的,你可去做实验。
3、不按记录存储原因:如果是当日文件,文件随时在增涨的。服务器上每日有上千个文件,每年该有多少,恐怕100G内存也缓存不过来的。
4、实际测试瓶颈只是在磁盘IO,检索处理和TCP传输只占1%时间。要分析究竟瓶颈在哪里才能做好优化。
的方法有一定效果,但效率提升有限。
目录:
data/demo/20080721/L101.ia.txt
...
data/demo/20080722/L101.ia.txt
...
data/demo/20080724/L101.ia.txt
...
data/demo/20080725/L101.ia.txt
...测试时,客户访问服务器程序时,服务程序其实已经访问过当日目录,并向data/demo/20080725/L101.ia.txt写入过数据,因此目录就只有最后的日子目录不同我还测试过第一次读data/demo/20080724/L101.ia.txt,第二次读data/demo/20080724/L101.ib.txt,同级目录的文件,差别并不大,就10几毫秒差别。为保证非缓冲,每次测试完后重新启动服务器。
谢谢您的热情回帖。数据库的性能瓶颈也在磁盘IO上SQL Server数据库中如果从100G中检索出某数据1小时数据(约3600条记录),恐怕1秒内不可能到达客户端的。而我现在的方法最大消耗时间是基本固定的,不管数据量,只要硬盘足够大,非缓冲条件下,1M也是800毫秒,320G也是800毫秒。而缓冲条件下最大只要8毫秒。写库就更快了,因为除第一次非缓冲外,其他都是缓冲。写库时间固定,平均每条记录写库时间0.25毫秒。当然是一次写多条记录。单独开的写库线程,时间足够及时写入的。
原因如下:磁盘系统在操作系统的管理下,本来就是异步的,为提高效率,操作系统是一次读一块到缓存,至于这个快有多大,不知道(估计每块128K)
但是知道的时读1M是的块数肯定要比读300K的块数多,块之间的时间间隔就取决于操作系统内系统进程、其他应用程序进程对磁盘
的访问要求了,如果其他程序也在访问,单个程序读盘是块之间的间隔就大了,这才是耗时的正真原因。因此减小文件大小才是提高
效率的方法。
实验结果表明,一次读8K和读1M是一样的,因为读第一个8k时操作系统才开始预读为什么选8K
因为我用1K,2K,4K,16K,64K,1M都做个实验,8K以后基本时间没得变化。第一读完文件,不管是每次读1个字节还是每次读1M字节,操作系统都缓存了。
第二次读其实都是从缓冲读。对于同一个文件,不存才实际中每次都访问磁盘的现象,除非文件内容改变了。
你第一次读取时,操作系统必需要处理,这个过程不可能少去的
就像操作指纹为了节省电能一样,有可能让磁盘低速转,这个时候你去操作,肯定会比较慢,但是一旦它正常转起来之后,速度就会快很多
如果说读300KB的文件需要110ms是正确的,那么读1MB的文件需要800ms这个数据就没有参考价值了,文件大小大约是1比3,所花时间的比例应该小于1比3才合理,如果是由于1MB的文件在磁盘中储存空间不连续,那就不该用这个文件来做实验了。
另外你可以试试把一个文件复制一份,看看需要多少时间,可以用大一些的文件(例如100MB)来试,目前的硬件速度复制100MB文件应该不会用到80s时间(我的电脑上测试不到5s)。复制文件要执行读和写,所以复制文件的时间肯定比单纯读文件的时间要长。
用连续的没有碎片的文件测试
1M大小文件(重起服务器保证非缓冲)
客户发送请求开始,服务器包括打开读取检索将结果约64K发送到客户端共耗时40-50毫秒之间多次非缓冲测试,均在40-50之间,性能基本能满足要求了。看来是我写文件产生的碎片太多造成频繁寻道浪费了IO时间。接下来就是解决写文件碎片的问题,方法有两种。一是预置文件大小(很多BT下载软件采用这个方法),二是文件最后一条记录写入后转存到其他磁盘(比如两个sataII接口磁盘物理盘,一个当工作盘,保存当日数据,另一个当历史数据盘,当日最后一个记录写完后就转存到历史盘)。日数据可能上几个G,因此当日数据全部缓存到内存是不可能的。
将测试文件重新拷贝后,保证每个文件连续,
然后重启服务器,保证非缓冲
下面客户端测试代码
5个文件,大小在490K到520K之间,是平时每日生成文件的典型大小
CTimeCount ct;//计时
ct.start();
nret = hdc.OpenRec("L101.ic","20080721","23000");
nret = hdc.OpenRec("L101.ib","20080721","230000");
nret = hdc.OpenRec("L101.ic","20080721","230000");
nret = hdc.OpenRec("L101.p","20080721","230000");
nret = hdc.OpenRec("L101.q","20080721","230000");
ct.end();
CString szTimeCount;
szTimeCount.Format("%.6f毫秒",ct.time_milli()/5);
MessageBox(szTimeCount);测试结果:每个文件平均耗时18.56毫秒,速度够用了,每秒可服务50个文件了。或者每秒可为50个用户提供一次服务了。如果数据在缓冲中,速度还可提高一倍。因生成的文件大小波动范围很大,因此不采用预置空间,而是采用双硬盘,一个硬盘单独存储历史数据,用一个线程来转存数据到历史盘,保证没得文件碎片。
"用户对日志数据的访问",存在倾向性么,即有没有规律,是否满足局部性原理?
如果用户倾向访问大量日志中的某一部分数据,可以考虑在内存中做一层Cache。减少访问IO的次数。
文件大小不固定,所以不使用预留。而使用重新拷贝,以消除文件碎片。这确实是个很实用的技术。以前只知道内存碎片会造成很多问题,比如Cache miss率下降,申大块内存不够等,这次看到了类似的文件碎片带来的问题,受益匪浅。学习了~