Read-in and Output Optimization

Keywords: iOS ascii Windows

By default, std::cin/std::cout is a very slow input/output mode, and scanf/printf is much faster than std::cin/std::cout.

But why?Is there any way to solve the problem of slow input and output?

Close Sync/Unbind

std::ios::sync_with_stdio(false)

This function is a "whether stdio is compatible" switch. C++ in order to be compatible with C, ensures that the program does not get confused when using printf and std::cout, and binds the output streams together.

This is actually a conservative measure taken by C++ for compatibility.We can unbind stdio before IO operations, but after doing so, be aware that std::cin/std::cout and scanf/printf cannot be used together.

tie

tie is a function that binds two streams and returns the current output stream pointer if the parameter is empty.

By default, std::cin binds to std::cout, which calls flush() every time the << operator is executed, which increases the IO burden.You can use std::cin.tie(0) (0 means NULL) to unbind std::cin from std::cout to further speed up execution.

code implementation

std::ios::sync_with_stdio(false);
std::cin.tie(0);
// std::cin.tie(nullptr);

Read-in optimization

Scnf and printf still have room for optimization, which is what this chapter covers -- read-in and output optimization.

  • Note that the read and output optimizations described on this page are for integer data, and to support other types of data, such as floating point numbers, you can write your own code following the optimization principles described on this page.

principle

It is well known that getchar is a function that reads in 1 byte of data and converts it to a char type, and it is so fast that you can use Read Character - Convert to Integer instead of slow reading

Each integer consists of two parts - Symbols and numbers

The'+'of integers is usually omitted and will not affect the value represented by the following numbers, while'-' cannot be omitted, so the decision needs to be made

A decimal integer contains no spaces or other characters except 0~9 and plus or minus signs, so when you read a character that should not exist in an integer (usually a space), you can tell that the reading is complete

The function isdigit is provided in the ctype.h and cctype header files for C++, respectively, which checks whether the incoming parameter is a decimal digit character, returns true, or false.Correspondingly, in the code below, isdigit(ch) can be used instead of Ch >='0'& & ch <='9', and isdigit(ch) can be used instead of Ch <'0' | ch >'9'

code implementation

int read() {
  int x = 0, w = 1;
  char ch = 0;
  while (ch < '0' || ch > '9') {  // When ch is not a number
    if (ch == '-') w = -1;        // Determine whether it is negative
    ch = getchar();               // Continue reading
  }
  while (ch >= '0' && ch <= '9') {  // When ch is a number
    x = x * 10 + (ch - '0');  // Add the newly read number'plus'after x
    // x is of type int, ch and'0'of type char are automatically converted to their corresponding
    // ASCII code, equivalent to converting ch to a corresponding number
    // Instead of x*10, you can also use the (x< 3) + (x< 1) notation here.
    ch = getchar();  // Continue reading
  }
  return x * w;  // Number*Positive and Negative Sign=Actual Value
}
  • Give an example

Reading num can be written as num=read();

Output optimization

principle

It is also well known that putchar is a function used to output a single character

So convert each digit into a character output to speed up

It is important to note that the negative sign separately determines the output, and each time the%(mod) takes out the last digit, so the output is in reverse order.

code implementation

int write(int x) {
  if (x < 0) {  // Negative + Output Negative + Variant is Positive
    x = -x;
    putchar('-');
  }
  if (x > 9) write(x / 10);  // Recursive, putting all but the last bit into the recursive output
  putchar(x % 10 + '0');  // All digits before the end of x have been output (recursively), output the end
}

However, the recursive implementation constant is large, and we can write a stack to accomplish this process

inline void write(int x) {
  static int sta[35];
  int top = 0;
  do {
    sta[top++] = x % 10, x /= 10;
  } while (x);
  while (top) putchar(sta[--top] + 48);  // 48 is'0'
}
  • Give an example

The output num can be written as write(num);

Faster read/output optimization

Faster reads can be achieved through fread or mmap.It essentially reads input files into a huge cache at once, much faster than reading them character by character (getchar, putchar).Because hard disks read and write more than once than in memory, it is much faster to read into and out of the cache first.

fread is more versatile because mmap cannot be used in a Windows environment.

fread is similar to scanf with the parameter'%s', but it is faster and can read several characters at once (including tabs such as space wraps), or even the entire file at once if the cache is large enough.

For output, we also have a corresponding fwrite function

std::size_t fread(void* buffer, std::size_t size, std::size_t count,
                  std::FILE* stream);
std::size_t fwrite(const void* buffer, std::size_t size, std::size_t count,
                   std::FILE* stream);

Example usage: fread(Buf, 1, SIZE, stdin), which represents reading a SIZE 1 byte block from a stdin file stream into a Buf.

Read-in usage is similar to normal read-in optimization, just redefine the getchar.Instead of reading a char from a file, it now reads a char from a Buf, where the head pointer moves one bit backwards.

char buf[1 << 20], *p1, *p2;
#define gc()                                                               \
  (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2) \
       ? EOF                                                               \
       : *p1++)

Fwrite is similar, putting an OutBuf[MAXSIZE] in it first, and then using fwrite to output the OutBuf at once.

Reference code:

namespace IO {
const int MAXSIZE = 1 << 20;
char buf[MAXSIZE], *p1, *p2;
#define gc()                                                               \
  (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, MAXSIZE, stdin), p1 == p2) \
       ? EOF                                                               \
       : *p1++)
inline int rd() {
  int x = 0, f = 1;
  char c = gc();
  while (!isdigit(c)) {
    if (c == '-') f = -1;
    c = gc();
  }
  while (isdigit(c)) x = x * 10 + (c ^ 48), c = gc();
  return x * f;
}
char pbuf[1 << 20], *pp = pbuf;
inline void push(const char &c) {
  if (pp - pbuf == 1 << 20) fwrite(pbuf, 1, 1 << 20, stdout), pp = pbuf;
  *pp++ = c;
}
inline void write(int x) {
  static int sta[35];
  int top = 0;
  do {
    sta[top++] = x % 10, x /= 10;
  } while (x);
  while (top) push(sta[--top] + '0');
}
}  // namespace IO

Buffering of input and output

Pritf and scanf have buffers.That's why if an input function follows an output function/an output function follows an input function, it can cause errors.

refresh buffer

  1. Program End
  2. Close File
  3. When printf outputs\r or\n to the terminal (Note: Buffers will not be refreshed if output to file)
  4. Manual fflush()
  5. Buffer full auto refresh
  6. cout output endl

Making input and output optimization more general

If your program uses multiple types of variables, you may need to write multiple input-output optimization functions.The code given below uses template in C++. Implements input and output optimization for all integer types.

template <typename T>
inline T
read() {  //Declare the template class, require input type T, and define the inline function read() with this type
  T sum = 0, fl = 1;  //Define sum,fl, and ch as input types
  int ch = getchar();
  for (; !isdigit(ch); ch = getchar())
    if (ch == '-') fl = -1;
  for (; isdigit(ch); ch = getchar()) sum = sum * 10 + ch - '0';
  return sum * fl;
}

If you want to enter variable a of type int, variable b of type long and variable uVariable c of type int128, then it can be written as

a = read<int>();
b = read<long long>();
c = read<__int128>();

Full with debug

Use FREAD (), fwrite() when you turn off the debug switch, and automatically destruct fwrite() when you exit.

Use getchar (), putchar () when turning on the debug switch for easy debugging.

To open a file for reading and writing, add freopen() before all reading and writing.

// #define DEBUG 1 //debug switch
struct IO {
#define MAXSIZE (1 << 20)
#define isdigit(x) (x >= '0' && x <= '9')
  char buf[MAXSIZE], *p1, *p2;
  char pbuf[MAXSIZE], *pp;
#if DEBUG
#else
  IO() : p1(buf), p2(buf), pp(pbuf) {}
  ~IO() { fwrite(pbuf, 1, pp - pbuf, stdout); }
#endif
  inline char gc() {
#if DEBUG //Debug, can display characters
    return getchar();
#endif
    if (p1 == p2) p2 = (p1 = buf) + fread(buf, 1, MAXSIZE, stdin);
    return p1 == p2 ? ' ' : *p1++;
  }
  inline bool blank(char ch) {
    return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
  }
  template <class T>
  inline void read(T &x) {
    register double tmp = 1;
    register bool sign = 0;
    x = 0;
    register char ch = gc();
    for (; !isdigit(ch); ch = gc())
      if (ch == '-') sign = 1;
    for (; isdigit(ch); ch = gc()) x = x * 10 + (ch - '0');
    if (ch == '.')
      for (ch = gc(); isdigit(ch); ch = gc())
        tmp /= 10.0, x += tmp * (ch - '0');
    if (sign) x = -x;
  }
  inline void read(char *s) {
    register char ch = gc();
    for (; blank(ch); ch = gc())
      ;
    for (; !blank(ch); ch = gc()) *s++ = ch;
    *s = 0;
  }
  inline void read(char &c) {
    for (c = gc(); blank(c); c = gc())
      ;
  }
  inline void push(const char &c) {
#if DEBUG //Debug, can display characters
    putchar(c);
#else
    if (pp - pbuf == MAXSIZE) fwrite(pbuf, 1, MAXSIZE, stdout), pp = pbuf;
    *pp++ = c;
#endif
  }
  template <class T>
  inline void write(T x) {
    if (x < 0) x = -x, push('-');  // Negative Output
    static T sta[35];
    T top = 0;
    do {
      sta[top++] = x % 10, x /= 10;
    } while (x);
    while (top) push(sta[--top] + '0');
  }
  template <class T>
  inline void write(T x, char lastChar) {
    write(x), push(lastChar);
  }
} io;

Reference resources

http://www.hankcs.com/program/cpp/cin-tie-with-sync_with_stdio-acceleration-input-and-output.html

http://meme.biology.tohoku.ac.jp/students/iwasaki/cxx/speed.html

Posted by geebo on Mon, 25 May 2020 18:13:00 -0700