我为我的OS类编写了这个:
#include <iostream>
#include <fstream>
//encodes a file using the (8,4) Hamming Code.
//usage : HammingEncode.out < inputFile > outputFile
int main() {
unsigned char const codebook[] = {0x00, 0x1E, 0x2D, 0x33, 0x4B, 0x55, 0x66, 0x78, 0x87, 0x99, 0xAA, 0xB4, 0xCC, 0xD2, 0xE1, 0xFF};
unsigned char in, nextByte;
unsigned char const leftMask = 0xF0, rightMask = 0x0F;
in = std::cin.get();
while (!std::cin.eof()) {
nextByte = (in & leftMask) >> 4;
std::cout << codebook[nextByte];
nextByte = in & rightMask;
std::cout << codebook[nextByte];
in = std::cin.get();
}
}
然后我决定在旧的Testamenet上测试它的速度(只是为了看看)。这是我们用Java教授的数据结构类的标准测试文件,我们可以对其进行排序,并且霍夫曼基本上没时间对其进行编码,但这需要相当长的时间进行编码。发生了什么事?
答案 0 :(得分:6)
std::cin
在文本模式下打开,因此它一直在寻找各种要注意的事物(如换行符等)。
鉴于std::cin
输入流不断嗅闻,我不会感到惊讶,它需要更长的时间,但看起来确实有点过分。以下内容绕过iostream
并直接使用FILE
流可能会达到您的预期效果:
#include <cstdlib>
#include <cstdio>
int main(int argc, char *argv[])
{
static unsigned char const codebook[] =
{
0x00, 0x1E, 0x2D, 0x33, 0x4B, 0x55, 0x66, 0x78,
0x87, 0x99, 0xAA, 0xB4, 0xCC, 0xD2, 0xE1, 0xFF
};
for (int c = std::fgetc(stdin); c!=EOF; c=std::fgetc(stdin))
{
std::fputc(codebook[c >> 4], stdout);
std::fputc(codebook[c & 0x0F], stdout);
}
return EXIT_SUCCESS;
}
我在加载了a
到z
范围内的字符的10MB随机文件中测试了上面的完全代码,使用{{1}时结果非常长}和std::cin
。直接使用std::cout
流,差异是巨大。本答案中的所有代码均使用FILE
优化Apple LLVM version 5.1 (clang-503.0.38) (based on LLVM 3.4svn)
进行了测试。
使用-O3
流
FILE
使用time ./hamming < bigfile.txt > bigfile.ham
real 0m1.855s
user 0m1.812s
sys 0m0.041s
和std::cin
std::cout
将time ./hamming < bigfile.txt > bigfile.ham
real 0m23.819s
user 0m7.416s
sys 0m16.377s
和std::cin
与std::cout
一起使用
std::cout.sync_with_stdio(false);
总之, ouch 。值得注意的是在系统中花费的时间。如果我有机会使用time ./hamming < bigfile.txt > bigfile.ham
real 0m24.867s
user 0m7.705s
sys 0m17.118s
和std::istream::get()
方法更新此内容,但老实说,我并不期待任何奇迹。除非有一些魔法(对我而言,不是对他人)从put()
转换 off io xlat的方式,std::cin
流可能是一个合理的选择。我还没有调查过FILE
std::cin
是否也是一个可行的选择,但它可能也有希望。
修改:使用rdbuf()
使用streambuf迭代器类有一个显着的改进,因为它基本上绕过了所有内联slat垃圾,但它仍然不如std::istreambuf_iterator<char>
流有效:
FILE
结果:
#include <iostream>
#include <cstdlib>
#include <cstdio>
int main(int argc, char *argv[])
{
static unsigned char const codebook[] =
{
0x00, 0x1E, 0x2D, 0x33, 0x4B, 0x55, 0x66, 0x78,
0x87, 0x99, 0xAA, 0xB4, 0xCC, 0xD2, 0xE1, 0xFF
};
std::istreambuf_iterator<char> cin_it(std::cin), cin_eof;
std::for_each(cin_it, cin_eof, [](char c)
{
std::cout.put(static_cast<char>(codebook[static_cast<unsigned char>(c) >> 4]));
std::cout.put(static_cast<char>(codebook[static_cast<unsigned char>(c) & 0x0F]));
});
return EXIT_SUCCESS;
}
请注意,time ./hamming < bigfile.txt > bigfile.ham
real 0m6.062s
user 0m5.795s
sys 0m0.053s
现在与system
流结果相当,但FILE
中其余iostream模板的开销似乎是一个痛点(但仍然比另一个{更好} {1}}尝试)。你赢了一些,你输了一些= P
编辑:无缓冲的系统IO
为了完全公平,绕过所有运行时缓冲并完全依赖系统调用来做这种疯狂,以下值得注意:
user
正如您所料,结果是可怕的:
iostream
答案 1 :(得分:2)
通过进行2次小改动,我接近一个数量级的改进。
std::ios_base::synch_with_stdio(false)
(没有明显的差异,但影响通常是特定于实施的)更新的代码如下所示:
int main()
{
//encodes a file using the (8,4) Hamming Code.
//usage : HammingEncode.out < inputFile > outputFile
unsigned char const codebook[] = { 0x00, 0x1E, 0x2D, 0x33, 0x4B, 0x55, 0x66, 0x78, 0x87, 0x99, 0xAA, 0xB4, 0xCC, 0xD2, 0xE1, 0xFF };
unsigned char in, nextByte;
unsigned char const leftMask = 0xF0, rightMask = 0x0F;
std::stringstream os;
std::ios_base::sync_with_stdio(false);
in = std::cin.get();
while (std::cin) {
nextByte = (in & leftMask) >> 4;
os.put(codebook[nextByte]);
nextByte = in & rightMask;
os.put(codebook[nextByte]);
in = std::cin.get();
}
std::cout << os.rdbuf();
}
<强>更新强>
我尝试了另外一个实现 - 使用基础std::streambuf
。
在我的系统上,原始代码需要 14秒处理完整的King James圣经 - 大约4.3 MB
原始尝试中的代码需要 2.1秒来处理。
此新实施需要 0.71秒来处理同一文档。
int main()
{
//encodes a file using the (8,4) Hamming Code.
//usage : HammingEncode.out < inputFile > outputFile
unsigned char const codebook[] = { 0x00, 0x1E, 0x2D, 0x33, 0x4B, 0x55, 0x66, 0x78, 0x87, 0x99, 0xAA, 0xB4, 0xCC, 0xD2, 0xE1, 0xFF };
unsigned char in, nextByte;
unsigned char const leftMask = 0xF0, rightMask = 0x0F;
std::stringstream os;
std::ios_base::sync_with_stdio(false);
std::streambuf * pbuf = std::cin.rdbuf();
do {
in = pbuf->sgetc();
nextByte = (in & leftMask) >> 4;
os << codebook[nextByte];
nextByte = in & rightMask;
os << codebook[nextByte];
} while (pbuf->snextc() != EOF);
std::cout << os.rdbuf();
}
答案 2 :(得分:1)
C ++ iostreams对于效率相当低有不好的看法,尽管不同的数字表明它是相当的质量实施问题,而不是iostream的劣势。
无论如何,只是为了确保它不像慢速硬盘,你可以比较执行时间,例如。
cat file1 > file2
cource cat
会更快一些,因为它不会使数据大小翻倍。
然后尝试将您的代码的效率与以下内容进行比较:
#include <stdio.h>
#include <unistd.h>
int main()
{
unsigned char buffer[1024*1024]; // 1MB buffer should be enough
while (!eof(STDIN_FILENO)){
size_t len = read(STDIN_FILENO, &buffer[0], 1024*1024);
write(STDOUT_FILENO, &buffer[0], len);
write(STDOUT_FILENO, &buffer[0], len);
}
return 0;
}
编辑:
对不起,我的不好。尝试
#include <stdio.h>
#include <unistd.h>
int main()
{
unsigned char buffer[1024*1024]; // 1MB buffer should be enough
size_t len = read(STDIN_FILENO, &buffer[0], 1024*1024);
while(len > 0){
write(STDOUT_FILENO, &buffer[0], len);
write(STDOUT_FILENO, &buffer[0], len);
len = read(STDIN_FILENO, &buffer[0], 1024*1024);
}
return 0;
}
答案 3 :(得分:0)
如果我理解正确,对于您读取的每个字节,则写入2个字节。因此输出文件将是输入的双倍大小。如果您的输入足够大 - 总IO(读+ 2 *写)时间将是显着的。
在霍夫曼编码中并非如此 - 因为你的写作通常比你读的少,而且总的IO时间会低得多。
修改强> 正如 Blorgbeard 所说,它可能是缓冲的不同之处。 C ++也做缓冲,但可能是默认缓冲区比Java要小得多。此外,HDD磁头应该在一个位置读取文件之间不断跳转然后在另一个位置写入 - 这一事实会显着影响整体IO性能。
在任何情况下,编码都应该以块的形式完成,以确保顺序读取和写入大块