Предполагая, что измерения времени для примерно 1 000 000 миллионов символов ниже порога измерения и записи в std::cout
и stdout
выполняются с использованием формы, использующей массовую запись (например, std::cout.write(str, size)
), я предполагаю, что putchar_unlock()
тратит большую часть своего времени на обновление некоторая часть структур данных в дополнение к помещению символа. Другие операции массовой записи будут копировать данные в буфер массово (например, с использованием memcpy()
) и обновлять структуры данных внутри только один раз.
То есть коды будут выглядеть примерно так (это pidgeon-code, т.е. просто примерно показывает, что происходит; реальный код был бы, по крайней мере, немного сложнее):
int putchar_unlocked(int c) {
*stdout->put_pointer++ = c;
if (stdout->put_pointer != stdout->buffer_end) {
return c;
}
int rc = write(stdout->fd, stdout->buffer_begin, stdout->put_pointer - stdout->buffer_begin);
// ignore partial writes
stdout->put_pointer = stdout->buffer_begin;
return rc == stdout->buffer_size? c: EOF;
}
Вместо этого массовая версия кода делает что-то вроде этого (с использованием нотации C ++, поскольку легче быть разработчиком на C ++; опять же, это pidgeon-code):
int std::streambuf::write(char const* s, std::streamsize n) {
std::lock_guard<std::mutex> guard(this->mutex);
std::streamsize b = std::min(n, this->epptr() - this->pptr());
memcpy(this->pptr(), s, b);
this->pbump(b);
bool success = true;
if (this->pptr() == this->epptr()) {
success = this->this->epptr() - this->pbase()
!= write(this->fd, this->pbase(), this->epptr() - this->pbase();
// also ignoring partial writes
this->setp(this->pbase(), this->epptr());
memcpy(this->pptr(), s + b, n - b);
this->pbump(n - b);
}
return success? n: -1;
}
Второй код может выглядеть немного сложнее, но выполняется только один раз для 30 символов. Большая часть проверок вынесена за пределы интересного. Даже если есть какая-то блокировка, она блокирует несогласованный мьютекс и не сильно препятствует обработке.
Особенно, когда не выполняется профилирование, цикл с использованием putchar_unlocked()
не будет сильно оптимизирован. В частности, код не будет векторизован, что приводит к немедленному коэффициенту, по крайней мере, примерно 3, но, вероятно, даже ближе к 16 в фактическом цикле. Стоимость замка быстро уменьшится.
Кстати, просто для создания достаточно ровной площадки: помимо оптимизации вы также должны вызывать std::sync_with_stdio(false)
при использовании стандартных потоковых объектов C ++.
person
Dietmar Kühl
schedule
19.09.2015
n
? Жестко запрограммированная константа? Или используяstrlen()
? Кроме того, почему бы вам не использоватьfputs()
илиfwrite()
? - person user12205   schedule 19.09.2015N
- это количество строк. - person Filipe Gonçalves   schedule 19.09.2015putchar_unlocked()
должен заблокироватьstdout
. Вам, вероятно, следует обращаться к _3 _ / _ 4_ во время циклаfor
, если только тот, кто вызываетwrite_str()
, не берет на себя эту ответственность. - person Michael Burr   schedule 19.09.2015write_str( char s[], int n )
простымwrite( 1, s, ( size_t ) n );
- person Andrew Henle   schedule 20.09.2015