我在一些C++中为嵌入式系统包装用户空间linux套接字功能(是的,这可能会再次重新发明轮子).
我想使用向量提供读写实现.
写入非常简单,我可以通过&myvec[0]并避免不必要的复制.我想做同样的事情并直接读入一个向量,而不是读入一个char缓冲区,然后将所有这些复制到一个新创建的向量中.
现在,我知道我想读多少数据,我可以适当地分配(vec.reserve()).我也可以读到&myvec[0],虽然这可能是一个非常糟糕的想法.显然这样做不允许myvec.size返回任何合理的东西.有没有办法做到这一点:
char *样式缓冲区到C++向量.我经常看看Vim中的帮助文件,但有时我想全屏阅读.由于:help命令在一个新窗口中打开它,并关闭旧窗口,如果它是帮助文件中唯一的一个,由于某种原因关闭了Vim,我发现这样做的唯一方法就是打开帮助文件,然后在新选项卡中重新打开它.
我想知道,有没有办法让:help命令(或其他命令)在同一个窗口中打开一个帮助文件,但是一个新的缓冲区?
我正在设计一个Buffer类,其目的是代表一块内存.
我的底层缓冲区是一个char*(好吧,boost::shared_array<char>实际上,但它并不重要).
我一直在决定为我的构造函数选择什么样的原型:
我应该去:
Buffer(const void* buf, size_t buflen);
Run Code Online (Sandbox Code Playgroud)
或者:
Buffer(const char* buf, size_t buflen);
Run Code Online (Sandbox Code Playgroud)
或者是其他东西 ?
通常做什么,为什么?
我需要在字节缓冲区中提取各种字段.我想出了这个解决方案:
func (fs *FileSystem) readSB() {
// fs.f is a *os.File
buf := make([]byte, 1024)
fs.f.ReadAt(buf, 1024)
// Offset: type
var p *bytes.Buffer
// 0: uint32
p = bytes.NewBuffer(buf[0:])
binary.Read(p, binary.LittleEndian, &fs.sb.inodeCount)
// 4: uint32
p = bytes.NewBuffer(buf[4:])
binary.Read(p, binary.LittleEndian, &fs.sb.blockCount)
// 20: uint32
p = bytes.NewBuffer(buf[20:])
binary.Read(p, binary.LittleEndian, &fs.sb.firstDataBlock)
// 24: uint32
p = bytes.NewBuffer(buf[24:])
binary.Read(p, binary.LittleEndian, &fs.sb.blockSize)
fs.sb.blockSize = 1024 << fs.sb.blockSize
// 32: uint32
p = bytes.NewBuffer(buf[32:])
binary.Read(p, binary.LittleEndian, &fs.sb.blockPerGroup)
// 40: uint32
p = bytes.NewBuffer(buf[40:])
binary.Read(p, …Run Code Online (Sandbox Code Playgroud) 当我寻找文件中的某个位置并写入少量数据(20个字节)时,幕后会发生什么?
我的理解
据我所知,可以从磁盘写入或读取的最小数据单元是一个扇区(传统上是512字节,但该标准现在正在改变).这意味着要写20个字节,我需要读取整个扇区,在内存中修改一些并将其写回磁盘.
这是我期望在无缓冲I/O中发生的事情.我还希望缓冲的I/O做大致相同的事情,但要聪明一点.所以我会想到,如果我通过随机搜索和写入来打破窗口的局部性,缓冲和无缓冲的I/O应该具有相似的性能......也许无缓冲的出现稍微好一些.
然后,我知道缓冲I/O仅缓冲一个扇区是疯狂的,所以我也可能期望它执行得非常糟糕.
我的应用程序
我正在存储由SCADA设备驱动程序收集的值,该驱动程序接收超过十万点的远程遥测.文件中有额外的数据,每条记录是40个字节,但在更新期间只需要写入20个字节.
实施前基准
为了检查我是否不需要想出一些出色的过度设计的解决方案,我已经使用写入文件的几百万个随机记录进行测试,该文件可能包含总共200,000条记录.每个测试都使用相同的值对随机数发生器进行种子处理.首先,我擦除文件并将其填充到总长度(大约7.6兆),然后循环几百万次,将随机文件偏移量和一些数据传递给两个测试函数之一:
void WriteOldSchool( void *context, long offset, Data *data )
{
int fd = (int)context;
lseek( fd, offset, SEEK_SET );
write( fd, (void*)data, sizeof(Data) );
}
void WriteStandard( void *context, long offset, Data *data )
{
FILE *fp = (FILE*)context;
fseek( fp, offset, SEEK_SET );
fwrite( (void*)data, sizeof(Data), 1, fp );
fflush(fp);
}
Run Code Online (Sandbox Code Playgroud)
也许没有惊喜?
这个OldSchool方法排在首位 - 很多.它的速度提高了6倍(148万,而每秒232000条记录).为了确保我没有遇到硬件缓存,我将数据库大小扩展到了2000万条记录(文件大小为763兆字节)并得到了相同的结果.
在你指出明显的呼吁之前fflush,让我说删除它没有任何效果.我想这是因为当我寻找足够远的地方时必须提交缓存,这正是我大部分时间都在做的事情.
发生什么了?
在我看来,每当我尝试写入时,缓冲的I/O必须读取(并且可能全部写入)大块文件.因为我几乎没有利用它的缓存,这非常浪费.
另外(我不知道磁盘上硬件缓存的细节),如果缓冲I/O在我只改变一个扇区时试图编写一堆扇区,那么会降低硬件缓存的有效性.
是否有任何磁盘专家可以比我的实验结果更好地发表评论和解释?=)
我正在考虑和HENRI COOK一样的问题.据我们从简短描述中可以看出,它被报告为Apache Jira上的一个错误.
我的问题实质上是事件只在应用程序关闭时记录(甚至在事件发生后几周).当记录量非常低时会发生这种情况.我在Windows Server 2008 R2上看到了这一点.这可以防止我们捕获并对生产错误做出反应.
现在,appender不是缓冲的.默认情况下,每次附加消息时,它还会在基础流上调用Flush().
我的问题是为什么不冲洗?除了以编程方式刷新所有appender之外,还有什么补救措施吗?你会认为一个脉动的appender是一个可行的解决方案吗?
appender配置:
<appender name="RollingErrorFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="File" value="D:\LogFiles\zzzz\xxxxxx__ERROR" />
<param name="AppendToFile" value="true" />
<param name="DatePattern" value="_yyyyMMddHH".log"" />
<param name="RollingStyle" value="Date" />
<param name="StaticLogFileName" value="false" />
<filter type="log4net.Filter.LevelRangeFilter">
<param name="LevelMin" value="ERROR" />
<param name="LevelMax" value="FATAL" />
</filter>
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%utcdate{yyyy-MM-dd HH:mm:ss.fff},[%thread],%level,%logger,%m%n"/>
</layout>
</appender>
Run Code Online (Sandbox Code Playgroud)
更新2013-06-19
我无法用任何代码重现行为.无论我尝试多么糟糕,数据总是立即写入磁盘.但是,进行了一项重要的观察:如果对文件的第一次写入大于1KiB,则修改时间永远不会随后写入更新.只有在关闭时关闭文件时才会更新它.另一方面,如果第一次写入是短的单行,则任何后续写入都将更新修改时间.这种行为在log4net和手动IO操作之间,在32位WinXP和64位W2k8R2之间,在.NET 2.0,3.5和.NET 4.0之间是一致的.这仍然没有解决问题,但至少我现在可以理解奇怪的修改时间模式.
谢谢,罗布
在C中,我可以轻松地将流设置为无缓冲的I/O:
FILE * f = fopen( "test", "r" );
setvbuf( f, (char *)NULL, _IONBF, 0 );
Run Code Online (Sandbox Code Playgroud)
如何使用C++ IOStream实现类似的无缓冲I/O?
我正在寻找一种更快的方法来完成这个:
int is_empty(char * buf, int size)
{
int i;
for(i = 0; i < size; i++) {
if(buf[i] != 0) return 0;
}
return 1;
}
Run Code Online (Sandbox Code Playgroud)
我意识到我正在寻找一种微观优化,除非在极端情况下,但我知道存在更快的方法,我很好奇它是什么.
假设我有单个Vim选项卡显示9个缓冲区(同样分开,如表3x3).目前,从左上方窗口右下角一送,我必须按3,Ctrl+ W,J,然后3,Ctrl+ W,L.这是麻烦的,我想只能够按Ctrl+ 9转到第九窗口和Ctrl+ 3去第三窗口,等有没有简单的方法可以让我在地图Vim的这样呢?
有人可以更明确地解释缓冲区的概念吗?我知道缓冲区是存储字符的数据结构,以及从中读取数据的位置.冲洗缓冲区的想法是什么?
刷新缓冲区时,这是指写入存储在其中的字符的行为吗?
从文字:
To avoid the overhead of writing in response to each output request, the library uses the
buffer to accumulate the characters to be written, and flushes the buffer, by writing its
contents to the output device, only when necessary. By doing so, it can combine several
output operations into a single write.
Run Code Online (Sandbox Code Playgroud)
当提到"刷新"时,几乎使其听起来好像缓冲区正在写入但同时也被擦除.只是猜测.
那么,为了写入屏幕上的视图需要缓冲区刷新?
When our program writes its prompt to cout, that output goes into the buffer associated
with the standard output stream. Next, we attempt …Run Code Online (Sandbox Code Playgroud)