我在竞争性编程竞赛的解决方案中多次遇到过这段特殊的代码片段。我理解这段代码的基本用法可以超越时间限制,但我想更深入地理解它。我知道unistd.h可以访问系统调用包装器函数,例如fork,pipe和I / O原语(read,write,..)。
如果有人能够向我解释或指导可以帮助我进一步理解的资源,也会很棒。
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
class FastInput {
public:
FastInput() {
m_dataOffset = 0;
m_dataSize = 0;
m_v = 0x80000000;
}
uint32_t ReadNext() {
if (m_dataOffset == m_dataSize) {
int r = read(0, m_buffer, sizeof(m_buffer));
if (r <= 0) return m_v;
m_dataOffset = 0;
m_dataSize = 0;
int i = 0;
if (m_buffer[0] < '0') {
if (m_v != 0x80000000) {
m_data[m_dataSize++] = m_v;
m_v = 0x80000000;
}
for (; (i < r) && (m_buffer[i] < '0'); ++i);
}
for (; i < r;) {
if (m_buffer[i] >= '0') {
m_v = m_v * 10 + m_buffer[i] - 48;
++i;
} else {
m_data[m_dataSize++] = m_v;
m_v = 0x80000000;
for (i = i + 1; (i < r) && (m_buffer[i] < '0'); ++i);
}
}
}
return m_data[m_dataOffset++];
}
public:
uint8_t m_buffer[32768];
uint32_t m_data[16384];
size_t m_dataOffset, m_dataSize;
uint32_t m_v;
};
class FastOutput {
public:
FastOutput() {
m_dataOffset = 0;
}
~FastOutput() {
}
void Flush() {
if (m_dataOffset) {
if (write(1, m_data, m_dataOffset));
m_dataOffset = 0;
}
}
void PrintUint(uint32_t v, char d) {
if (m_dataOffset + 11 > sizeof(m_data)) Flush();
if (v < 100000) {
if (v < 1000) {
if (v < 10) {
m_data[m_dataOffset + 0] = v + 48;
m_dataOffset += 1;
} else if (v < 100) {
m_data[m_dataOffset + 1] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 0] = v + 48;
m_dataOffset += 2;
} else {
m_data[m_dataOffset + 2] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 1] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 0] = v + 48;
m_dataOffset += 3;
}
} else {
if (v < 10000) {
m_data[m_dataOffset + 3] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 2] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 1] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 0] = v + 48;
m_dataOffset += 4;
} else {
m_data[m_dataOffset + 4] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 3] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 2] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 1] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 0] = v + 48;
m_dataOffset += 5;
}
}
} else {
if (v < 100000000) {
if (v < 1000000) {
m_data[m_dataOffset + 5] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 4] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 3] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 2] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 1] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 0] = v + 48;
m_dataOffset += 6;
} else if (v < 10000000) {
m_data[m_dataOffset + 6] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 5] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 4] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 3] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 2] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 1] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 0] = v + 48;
m_dataOffset += 7;
} else {
m_data[m_dataOffset + 7] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 6] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 5] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 4] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 3] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 2] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 1] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 0] = v + 48;
m_dataOffset += 8;
}
} else {
if (v < 1000000000) {
m_data[m_dataOffset + 8] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 7] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 6] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 5] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 4] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 3] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 2] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 1] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 0] = v + 48;
m_dataOffset += 9;
} else {
m_data[m_dataOffset + 9] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 8] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 7] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 6] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 5] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 4] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 3] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 2] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 1] = v - v / 10 * 10 + 48;
v /= 10;
m_data[m_dataOffset + 0] = v + 48;
m_dataOffset += 10;
}
}
}
m_data[m_dataOffset++] = d;
}
void PrintChar(char d) {
if (m_dataOffset + 1 > sizeof(m_data)) Flush();
m_data[m_dataOffset++] = d;
}
void ReplaceChar(int offset, char d) {
m_data[m_dataOffset + offset] = d;
}
public:
uint8_t m_data[32768];
size_t m_dataOffset;
};
还有一件事:在生产级代码中采用类似技术是一种好习惯吗?
答案 0 :(得分:13)
在生产级代码中采用类似技术是否是一种好习惯?
没有。重新实现轮子会导致错误。错误需要额外的开发时间和成本。
可以帮助我进一步理解它。
如果您不理解代码,则代码编写得很糟糕。代码由人类和人类编写。如果另一个程序员不能快速理解代码,则可能存在很大问题。这种思维背后的基本原理(“为人类编写”)很简单:开发时间成本很高,而且不可读的代码会增加开发时间。
有问题的代码片段使用了几种不良编码实践:
m_v
什么意思而不读取程序的其余部分吗?)+ 48
,+ 11
)v /= 10
和类似代码 - C ++有宏/模板,该死的,所以如果你想手动展开循环,请使用它们!)。 除非你想在编程上变得更糟,否则我建议避免试图“理解”这段代码。这是坏的。
我严重怀疑这个特殊的设计是剖析的结果。最有可能的情况是某些“天才”认为他的代码片段将胜过内置函数。
如果您想要表现,请遵循以下模式:
答案 1 :(得分:2)
在PrintUint
函数中,他基本上只是手动展开循环。展开循环有时是一件好事 - 但是编译器已经做到了,并且在大多数情况下会比你做得更好。
要插入我最喜欢的语言功能,最好使用模板实现:一个简单的实现(可能更聪明)看起来像:
// I'm sure the compiler can figure out the inline part, but I'll add it anyways
template<unsigned int N>
inline void print_uint_inner(uint32_t v) {
m_data[m_dataOffset + N] = v - v / 10 * 10 + 48;
print_uint_inner<N-1>(v / 10);
}
// For situations just like this, there's a trick to avoid having to define the base case separately.
inline void print_uint_inner<0>(uint32_t v) {
m_data[m_dataOffset] = v - v / 10 * 10 + 48;
}
template<unsigned int N>
inline void print_uint_helper(uint32_t v) {
print_uint_inner<N-1>(v);
m_dataOffset += N;
}
// We could generate the compile-time binary search with templates too, rather than by hand.
void PrintUint(uint32_t v, char d) {
if (m_dataOffset + 11 > sizeof(m_data)) Flush();
if (v < 100000) {
if (v < 1000) {
if (v < 10) {
print_uint_helper<1>(v);
} else if (v < 100) {
print_uint_helper<2>(v);
} else {
print_uint_helper<3>(v);
}
} else {
if (v < 10000) {
print_uint_helper<4>(v);
} else {
print_uint_helper<5>(v);
}
}
} else {
if (v < 100000000) {
if (v < 1000000) {
print_uint_helper<6>(v);
} else if (v < 10000000) {
print_uint_helper<7>(v);
} else {
print_uint_helper<8>(v);
}
} else {
if (v < 1000000000) {
print_uint_helper<9>(v);
} else {
print_uint_helper<10>(v);
}
}
}
m_data[m_dataOffset++] = d;
}
一般来说,这样的好编码实践吗?是的,但只有满足以下所有条件时才会这样做:
此外,您应该保留使用编译时常量或预处理器指令切换回简单版本的能力。这有两个重要原因:
答案 2 :(得分:2)
尝试更快的I / O
<强>的ios_base :: sync_with_stdio(假); cin.tie(NULL); 强>
它设置在每次输入/输出操作之后标准C ++流是否与标准C流同步。默认情况下,iostream对象和cstdio流是同步的。