|
前些日子, 测试提单, 直接写文件的情形下, 日志生成太慢, 影响体验 老代码(历史遗留代码): 尝试过各种可能问题的修改, 都不见效, 最后考虑换函数, 改用C++的ofstream: …… |
|
| 1分 |
这不科学啊。
|
| 2分 |
有缓冲的IO函数会好一些。
|
| 2分 |
摒弃fstream
使用FILE * |
|
换固态硬盘再试试?
|
|
|
现实就是这样啊 |
|
|
下午再重新写个测试 给各位看看
|
|
| 1分 |
难道fstream……….有更优秀的实现?
|
| 2分 |
因为ofstream的flush清的是自己的缓存,东西可能留在os的缓存里没写进磁盘。当然会更快……
|
|
非常感谢你的解答! |
|
|
下午又写找时间偷偷写了个测试代码:
#ifdef _MSC_VER
#include <sys/stat.h>
#include <io.h>
#include <Windows.h>
#else
#include <unistd.h>
#endif
#include <fcntl.h>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#ifdef _MSC_VER
#define OPEN(fd, file) \
fd = open(file, O_CREAT | O_RDWR | O_APPEND, S_IWRITE | S_IREAD)
#define WRITE(fd, data, len) \
_write(fd, pszData, iDataLen)
#define FLUSH(fd) \
_commit(fd)
#define CLOSE(fd) \
_close(fd)
#define SLEEP(s) \
Sleep(1000 * s)
#else
#define OPEN(fd, file) \
umask(002); \
fd = open(file, O_CREAT | O_RDWR | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP)
#define WRITE(fd, data, len) \
write(fd, pszData, iDataLen);
#define FLUSH(fd) \
fsync(fd)
#define CLOSE(fd) \
close(fd)
#define SLEEP(s) \
sleep(s)
#endif
static int s_iFd = -1;
static FILE * s_pFile = NULL;
static std::ofstream s_ofs;
enum TEST_TYPE
{
TYPE_MIN,
TYPE_HANDLE = TYPE_MIN,
TYPE_FILE,
TYPE_OFSTREAM,
TYPE_MAX
};
const char * GetType(enum TEST_TYPE enType)
{
switch (enType)
{
case TYPE_HANDLE:
{
return("handle");
}
case TYPE_FILE:
{
return("FILE");
}
case TYPE_OFSTREAM:
{
return("ofstream");
}
default:
{
return("unknown");
}
}
};
void OpenFile(enum TEST_TYPE enType)
{
switch (enType)
{
case TYPE_HANDLE:
{
OPEN(s_iFd, "a.txt");
if (s_iFd < 0)
{
fprintf(stderr, "open failed\n");
exit(1);
}
break;
}
case TYPE_FILE:
{
s_pFile = fopen("b.txt", "w+");
if (NULL == s_pFile)
{
fprintf(stderr, "fopen failed\n");
exit(2);
}
break;
}
case TYPE_OFSTREAM:
{
s_ofs.open("c.txt", std::ios::binary | std::ios::app);
if (!s_ofs)
{
fprintf(stderr, "ofstream::open failed\n");
exit(3);
}
break;
}
default:
{
fprintf(stderr, "open: unknown type\n");
exit(4);
break;
}
}
}
void WriteFile(enum TEST_TYPE enType, const char * pszData, int iDataLen)
{
if (NULL == pszData || 0 >= iDataLen)
{
return;
}
switch (enType)
{
case TYPE_HANDLE:
{
while (0 < iDataLen)
{
int iWirteLen = WRITE(s_iFd, pszData, iDataLen);
if (0 > iWirteLen)
{
fprintf(stderr, "write failed\n");
break;
}
pszData += iWirteLen;
iDataLen -= iWirteLen;
}
FLUSH(s_iFd);
break;
}
case TYPE_FILE:
{
while (0 < iDataLen)
{
int iWirteLen = fwrite(pszData, iDataLen, 1, s_pFile);
if (0 > iWirteLen)
{
fprintf(stderr, "fwrite failed\n");
break;
}
pszData += iWirteLen;
iDataLen -= iWirteLen;
}
fflush(s_pFile);
break;
}
case TYPE_OFSTREAM:
{
s_ofs.write(pszData, iDataLen);
if (s_ofs.fail())
{
fprintf(stderr, "ofstream::write failed\n");
s_ofs.clear();
}
s_ofs.flush();
break;
}
default:
{
fprintf(stderr, "write: unknown type\n");
exit(5);
break;
}
}
}
void CloseFile(enum TEST_TYPE enType)
{
switch (enType)
{
case TYPE_HANDLE:
{
CLOSE(s_iFd);
break;
}
case TYPE_FILE:
{
fclose(s_pFile);
break;
}
case TYPE_OFSTREAM:
{
s_ofs.close();
break;
}
default:
{
fprintf(stderr, "close: unknown type\n");
exit(6);
break;
}
}
}
int main(int argc, char * argv[])
{
const char szData[] = "1234567890123456789012345678901234567890123456789012345678901234567890";
const int iDataLen = sizeof(szData) - 1;
enum TEST_TYPE enIndex = TYPE_MIN;
while (TYPE_MAX > enIndex)
{
const char * pszType = GetType(enIndex);
OpenFile(enIndex);
int iWriteCount = 0;
int iPrintCount = 0;
time_t tStartTime = time(NULL);
time_t tLastTime = tStartTime;
while (10 > iPrintCount)
{
WriteFile(enIndex, szData, iDataLen);
++iWriteCount;
if (time(NULL) > tLastTime)
{
++iPrintCount;
tLastTime = time(NULL);
fprintf(stdout, "type: %9s, speed: %9d\n", pszType,
(iWriteCount / (tLastTime - tStartTime)));
}
}
CloseFile(enIndex);
enIndex = static_cast<enum TEST_TYPE>(enIndex + 1);
SLEEP(2);
};
return(0);
}
|
|
|
下面是各平台的测试结果:
Linux: no-flush: flush: Windows: no-flush: flush: 依据这些打印数据, 可知: 有一点问题: 当然, 也许是我用错了函数导致的这种结果, 如果你有更合理的函数, 请告知! 非常感谢! PS: 现在CSDN贴的代码真丑! |
|
|
如果是突然断电,os缓存没有flush的话,你_write+_commit的会保存到磁盘,ofstream的就不会。而只是程序异常退出的话,两种都会保存到磁盘。
就看你要怎样程度的健壮性了。不存在ofstream秒杀另两个的说法,因为ofstream没有另两种健壮。 |
|
|
看完10楼的回复 才明白过来你说的是什么意思 |
|
|
谢谢你的讲解, 另外问一下: |
|
| 1分 |
缓存对性能有质的飞跃, 前提是你的程序最好别挂掉。
|
|
Handle的方式是不行了, 以后如果发现ofstream不行的话, 就改用FILE方式的, 谢谢 |
|
|
说明还有数据在缓存里啊。要么fstream的缓存要么os的缓存。 |
|
| 1分 |
信赵老师全身打滚。c库的FILE相关的操作也是有(和os缓存不同的)缓存的,用fflush就和你的ofstream.flush一样,不保证写入磁盘。 |
|
是的, 不过程序意外挂掉, 缓不缓冲, 日志都会有丢失的… |
|
|
但是 WINDOWS下: |
|
|
是否说明, WINDOWS下, FILE方式比ofstream可靠?
|
|
|
测试代码呢 |
|
| 8分 |
貌似你的fwrite用法有问题。这个函数返回的是count(对应第三个参数),不是size(对应第二个参数),也就是每次成功都返回1(因为你给的count是1)。这个返回值不会有负数,如果失败,返回一个比count小的值。 你试试把第二个参数和第三个参数交换一下 |
|
日志文件通常不会太大,也不会写得很频繁,速度不是主要问题。但是处理异常是日志的主要功能之一,不能不考虑。突然断电的可能性不大,但是异常退出很常见。
分析速度之前先要确保每个用法的结果都是正确的。结果不一致时应先检查程序的正确性,结果不正确的情况下比较速度没有意义。 分析程序运行速度的程序,要使用大规模数据。10条记录的时间说明不了太大问题,起码要有上万条记录。 |
|
|
谢谢, 这个就解释了我在 16, 22, 23楼的疑问了, 非常感谢! |
|
|
重新测试了下速度:
Linux + no_flush: handle慢一个数量级, FILE与ofstream同一数量级, 但FILE略快 type: handle, speed: 365147 type: handle, speed: 553700 type: handle, speed: 620208 type: handle, speed: 660457 type: handle, speed: 683569 type: handle, speed: 698560 type: handle, speed: 710284 type: handle, speed: 718802 type: handle, speed: 726606 type: handle, speed: 729462 type: FILE, speed: 4557587 type: FILE, speed: 4805500 type: FILE, speed: 4266081 type: FILE, speed: 4493705 type: FILE, speed: 4859939 type: FILE, speed: 5003800 type: FILE, speed: 5072787 type: FILE, speed: 5132573 type: FILE, speed: 4857114 type: FILE, speed: 4878231 type: ofstream, speed: 3706144 type: ofstream, speed: 4089290 type: ofstream, speed: 4019945 type: ofstream, speed: 3723549 type: ofstream, speed: 3517367 type: ofstream, speed: 3674459 type: ofstream, speed: 3925795 type: ofstream, speed: 4079569 type: ofstream, speed: 3827552 type: ofstream, speed: 3762736 Linux + flush: handle慢四个数量级, FILE与ofstream同一数量级, 但FILE略快 type: handle, speed: 19 type: handle, speed: 29 type: handle, speed: 37 type: handle, speed: 37 type: handle, speed: 36 type: handle, speed: 36 type: handle, speed: 36 type: handle, speed: 37 type: handle, speed: 36 type: handle, speed: 38 type: FILE, speed: 627707 type: FILE, speed: 660472 type: FILE, speed: 670974 type: FILE, speed: 678343 type: FILE, speed: 683366 type: FILE, speed: 687264 type: FILE, speed: 690667 type: FILE, speed: 691783 type: FILE, speed: 692458 type: FILE, speed: 693900 type: ofstream, speed: 623825 type: ofstream, speed: 641605 type: ofstream, speed: 657575 type: ofstream, speed: 653685 type: ofstream, speed: 663389 type: ofstream, speed: 667315 type: ofstream, speed: 668244 type: ofstream, speed: 623023 type: ofstream, speed: 624355 type: ofstream, speed: 630020 Windows + no_flush: 三种方式, 速度都在同一个数量级, handle慢一些, 另外两个各有快慢; type: handle, speed: 71736 type: handle, speed: 91307 type: handle, speed: 99067 type: handle, speed: 102946 type: handle, speed: 105018 type: handle, speed: 106260 type: handle, speed: 107252 type: handle, speed: 108264 type: handle, speed: 108958 type: handle, speed: 109396 type: FILE, speed: 500532 type: FILE, speed: 334409 type: FILE, speed: 246716 type: FILE, speed: 228381 type: FILE, speed: 219697 type: FILE, speed: 213099 type: FILE, speed: 212147 type: FILE, speed: 209722 type: FILE, speed: 205950 type: FILE, speed: 205227 type: ofstream, speed: 435828 type: ofstream, speed: 313958 type: ofstream, speed: 297525 type: ofstream, speed: 274231 type: ofstream, speed: 296749 type: ofstream, speed: 312720 type: ofstream, speed: 330095 type: ofstream, speed: 332609 type: ofstream, speed: 322173 type: ofstream, speed: 345480 Windows + flush: handle慢四个数量级, FILE比ofstream快一半 type: handle, speed: 22 type: handle, speed: 37 type: handle, speed: 42 type: handle, speed: 44 type: handle, speed: 44 type: handle, speed: 42 type: handle, speed: 41 type: handle, speed: 41 type: handle, speed: 40 type: handle, speed: 40 type: FILE, speed: 121989 type: FILE, speed: 125874 type: FILE, speed: 126775 type: FILE, speed: 127286 type: FILE, speed: 127311 type: FILE, speed: 127464 type: FILE, speed: 127694 type: FILE, speed: 127870 type: FILE, speed: 128062 type: FILE, speed: 127990 type: ofstream, speed: 81132 type: ofstream, speed: 80382 type: ofstream, speed: 80439 type: ofstream, speed: 80652 type: ofstream, speed: 80746 type: ofstream, speed: 80823 type: ofstream, speed: 80845 type: ofstream, speed: 80889 type: ofstream, speed: 80810 type: ofstream, speed: 80738 自己总结下: FILE比ofstream略快, 比handle快很多, 但是handle是无二次缓冲的IO, 文件刷入更及时 |
|
|
好了 谢谢各位 结贴了
|
|
|
仅供参考
#include <sys\stat.h>
#include <io.h>
#include <fcntl.h>
#include <share.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>
#define MAX_CLU_BYTES 65536
FILE *fo;
int fh;
__int64 offs,offs1;
__int64 rvi64;
int rv,wrv;
unsigned char buf[MAX_CLU_BYTES];
char ofn[_MAX_PATH];
char offstr[80];
void strcpybutcomma(char *t,char *s) {
char c;
while (1) {
c = *s++;
if ("",""!=c) *t++ = c;
if (0==c) break;
}
}
void main(int argc,char **argv) {
if (argc<3) {
printf("Copy File Tail.\n");
printf("Usage:\n");
printf(" cft filename.ext offset_begin[-offset_end]\n");
printf("Copy filename.ext offset_begin[-offset_end] to offset_begin[-offset_end]-filename.ext\n");
printf("Note: Byte at offset_end is NOT included.\n");
printf("Example:\n");
printf(" cft abc.rar 12345\n");
printf("Copy abc.rar offset 12345-end to 12345-abc.rar\n");
printf(" cft abc.rar 123-12345\n");
printf("Copy abc.rar offset 123-12345 to 123-12345-abc.rar\n");
printf(" cft abc.rar 0xAB-0xCD\n");
printf("Copy abc.rar offset 0xAB-0xCD to 0xAB-0xCD-abc.rar\n");
return;
}
strcpybutcomma(offstr,argv[2]);
rv=sscanf(offstr,"%I64i-%I64i",&offs,&offs1);
if (rv==0) {
printf("offset %s is not number\n",argv[2]);
return;
}
fh=_sopen(argv[1],_O_BINARY|_O_RDONLY|_O_RANDOM,_SH_DENYWR);
if (fh==-1) {
printf("_sopen %s errno=%d\n",argv[1],errno);
return;
}
if (rv==1) {
offs1=_filelengthi64(fh);
if (offs1==-1i64) {
printf("%I64=_filelengthi64 errno=%d\n",offs1,errno);
_close(fh);
return;
}
} else {//rv==2
if (offs1<offs) {
printf("%s offset_begin>offset_end error\n",argv[2]);
_close(fh);
return;
}
}
rvi64=_lseeki64(fh,offs,SEEK_SET);
if (rvi64!=offs) {
printf("%I64u=_lseeki64 %I64u errno=%d\n",rvi64,offs,errno);
_close(fh);
return;
}
sprintf(ofn,"%s-",offstr);
strcat(ofn,argv[1]);
fo=fopen(ofn,"wb");
if (fo==NULL) {
_close(fh);
printf("fopen %s error\n",ofn);
return;
}
cprintf("\n%I64u\r",offs);
while (1) {
rv=_read(fh,buf,(unsigned int)__min(offs1-offs,MAX_CLU_BYTES));
if (rv==0) break;//
if (rv<0) {
fclose(fo);
_close(fh);
printf("_read %s offset %I64u error\n",argv[1],offs);
return;
}
wrv=fwrite(buf,1,rv,fo);
if (wrv!=rv) {
fclose(fo);
_close(fh);
printf("fwrite %s error\n",ofn);
return;
} else {
offs+=rv;
cprintf("%I64u\r",offs);
if (offs>=offs1) break;//
}
}
fclose(fo);
_close(fh);
printf("Copy %s offset %s to %s OK.\n",argv[1],argv[2],ofn);
}
|
|
|
Windows下可以试试直接调用Win API:
WriteFile WriteFileEx |
|
|
直接用api估计更悲剧 |
|
|
不可能。因为在Windows下, ofstream::write() , fwrite() , write()最终都调用的是Win API。 再参考C:\Program Files\Microsoft Visual Studio 10.0\VC\crt\src\write.c |
|
|
所以才可能悲剧,这里面主要是系统API之上的各种缓存造成的,可以看看apue,虽然不是windows,但是原理是一样的 |
|
|
我以为你说“悲剧”是指Win API速度会比其它方案慢呢。
|
|
|
感谢楼上两位的回复.
|
|
|
你这完全是为了提高编程能力啊
|
|
|
我自己亲自经历过C++ iostream比C文件读写慢好多的情况。写文件操作绝对c文件读写效率高。
|
|