Skip to content

Commit 8bd872f

Browse files
jwakelyTomasz Kamiński
andcommitted
libstdc++: Implement P3107R5 optimizations for std::print [PR121790]
The names of the vprint functions follow the convention from P3235R3. This takes advantage of the additional permission proposed by P3107R5 so that std::print can write directly to a FILE stream, rather than formatting to an intermediate std::string temporary and then writing that to the stream. The change is to write to a new _File_sink type instead of a _Str_sink that populates a std::string. There are three implementations of _File_sink. For non-Glibc targets that support POSIX flockfile and putc_unlocked, the stream will be locked and then formatted characters will be buffered on the stack (instead of allocating a std::string) and copied to the stream when the buffer fills up. For Glibc, _File_sink will lock the stream but then if the file is line-buffered or fully buffered, characters will be written directly into the file's output buffer. This avoids two levels of buffering and copying the characters from one to the other. For an unbuffered stream (like stderr) the _File_sink buffer will still be used, to avoid the overhead of lots of small writes to the stream. Because this version of _File_sink accesses the stream's buffer directly it relies on glibc-specific implementation details that are exposed in public headers. A fallback definition of _File_sink just wraps a _Str_sink so is equivalent to the original code, and is used when flockfile isn't available. Both forms of std::println (taking a FILE* and a std::ostream) can be implemented more efficiently by appending a newline to the format string, to avoid formatting twice. PR libstdc++/121790 libstdc++-v3/ChangeLog: * acinclude.m4 (GLIBCXX_CHECK_STDIO_LOCKING): New macro to check for std::print dependencies. * config.h.in: Regenerate. * configure: Regenerate. * configure.ac: Use GLIBCXX_CHECK_STDIO_LOCKING. * include/bits/formatfwd.h (enable_nonlocking_formatter_optimization): Define new variable template. * include/bits/version.def (print): Bump value. * include/bits/version.h: Regenerate. * include/std/format (enable_nonlocking_formatter_optimization): Define specializations for variable template. * include/std/ostream (print) [!_WIN32]: Do not use vprint_unicode at all. (println): Append newline to format string instead of formatting twice. * include/std/print (_File_sink): New class. (vprint_nonunicode_locking): New function. (vprint_unicode_locking): New function reusing previous code from vprint_unicode. (vprintf_unicode): Defer to vprint_nonunicode for Windows or to vprint_unicode_locking otherwise. (print): [!_WIN32]: Do no use vprint_unicode at all. Check enable_nonlocking_formatter_optimization and defer to either vprint_nonunicode_locking or vprint_nonunicode. (println): Use vprint_unicode or format directly to a _File_sink instead of formatting twice. * testsuite/27_io/print/1.cc: Updated and added new tests. * testsuite/std/format/formatter/nonlocking.cc: New tests. Reviewed-by: Jonathan Wakely <jwakely@redhat.com> Reviewed-by: Tomasz Kamiński <tkaminsk@redhat.com> Co-authored-by: Tomasz Kamiński <tkaminsk@redhat.com>
1 parent 90dde80 commit 8bd872f

File tree

12 files changed

+733
-23
lines changed

12 files changed

+733
-23
lines changed

libstdc++-v3/acinclude.m4

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5804,6 +5804,89 @@ AC_DEFUN([GLIBCXX_CHECK_DEBUGGING], [
58045804
AC_LANG_RESTORE
58055805
])
58065806

5807+
dnl
5808+
dnl Check whether the dependencies for optimized std::print are available.
5809+
dnl
5810+
dnl Defines:
5811+
dnl _GLIBCXX_USE_STDIO_LOCKING if flockfile, putc_unlocked etc. are present.
5812+
dnl _GLIBCXX_USE_GLIBC_STDIO_EXT if FILE::_IO_write_ptr etc. are also present.
5813+
dnl
5814+
AC_DEFUN([GLIBCXX_CHECK_STDIO_LOCKING], [
5815+
AC_LANG_SAVE
5816+
AC_LANG_CPLUSPLUS
5817+
5818+
AC_MSG_CHECKING([whether flockfile and putc_unlocked are defined in <stdio.h>])
5819+
AC_TRY_COMPILE([
5820+
#include <stdio.h>
5821+
],[
5822+
FILE* f = ::fopen("", "");
5823+
::flockfile(f);
5824+
::putc_unlocked(' ', f);
5825+
::funlockfile(f);
5826+
::fclose(f);
5827+
], [ac_stdio_locking=yes], [ac_stdio_locking=no])
5828+
AC_MSG_RESULT($ac_stdio_locking)
5829+
5830+
if test "$ac_stdio_locking" = yes; then
5831+
AC_DEFINE_UNQUOTED(_GLIBCXX_USE_STDIO_LOCKING, 1,
5832+
[Define if flockfile and putc_unlocked should be used for std::print.])
5833+
5834+
# This is not defined in POSIX, but is present in glibc, musl, and Solaris.
5835+
AC_MSG_CHECKING([whether fwrite_unlocked is defined in <stdio.h>])
5836+
AC_TRY_COMPILE([
5837+
#include <stdio.h>
5838+
],[
5839+
FILE* f = ::fopen("", "");
5840+
::flockfile(f);
5841+
::fwrite_unlocked("", 1, 1, f);
5842+
::funlockfile(f);
5843+
::fclose(f);
5844+
], [ac_fwrite_unlocked=yes], [ac_fwrite_unlocked=no])
5845+
AC_MSG_RESULT($ac_fwrite_unlocked)
5846+
if test "$ac_fwrite_unlocked" = yes; then
5847+
AC_DEFINE(HAVE_FWRITE_UNLOCKED, 1,
5848+
[Define if fwrite_unlocked can be used for std::print.])
5849+
5850+
# Check for Glibc-specific FILE members and <stdio_ext.h> extensions.
5851+
case "${target_os}" in
5852+
gnu* | linux* | kfreebsd*-gnu | knetbsd*-gnu)
5853+
AC_MSG_CHECKING([for FILE::_IO_write_ptr and <stdio_ext.h>])
5854+
AC_TRY_COMPILE([
5855+
#include <stdio.h>
5856+
#include <stdio_ext.h>
5857+
extern "C" {
5858+
using f1_type = int (*)(FILE*) noexcept;
5859+
using f2_type = size_t (*)(FILE*) noexcept;
5860+
}
5861+
],[
5862+
f1_type twritable = &::__fwritable;
5863+
f1_type tblk = &::__flbf;
5864+
f2_type pbufsize = &::__fbufsize;
5865+
FILE* f = ::fopen("", "");
5866+
int i = ::__overflow(f, EOF);
5867+
bool writeable = ::__fwritable(f);
5868+
bool line_buffered = ::__flbf(f);
5869+
size_t bufsz = ::__fbufsize(f);
5870+
char*& pptr = f->_IO_write_ptr;
5871+
char*& epptr = f->_IO_buf_end;
5872+
::fflush_unlocked(f);
5873+
::fclose(f);
5874+
], [ac_glibc_stdio=yes], [ac_glibc_stdio=no])
5875+
AC_MSG_RESULT($ac_glibc_stdio)
5876+
if test "$ac_glibc_stdio" = yes; then
5877+
AC_DEFINE_UNQUOTED(_GLIBCXX_USE_GLIBC_STDIO_EXT, 1,
5878+
[Define if Glibc FILE internals should be used for std::print.])
5879+
fi
5880+
;;
5881+
*)
5882+
;;
5883+
esac
5884+
fi
5885+
fi
5886+
5887+
AC_LANG_RESTORE
5888+
])
5889+
58075890

58085891
# Macros from the top-level gcc directory.
58095892
m4_include([../config/gc++filt.m4])

libstdc++-v3/config.h.in

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@
152152
/* Define to 1 if you have the `frexpl' function. */
153153
#undef HAVE_FREXPL
154154

155+
/* Define if fwrite_unlocked can be used for std::print. */
156+
#undef HAVE_FWRITE_UNLOCKED
157+
155158
/* Define if getentropy is available in <unistd.h>. */
156159
#undef HAVE_GETENTROPY
157160

@@ -828,6 +831,9 @@
828831
/* Define if get_nprocs is available in <sys/sysinfo.h>. */
829832
#undef _GLIBCXX_USE_GET_NPROCS
830833

834+
/* Define if Glibc FILE internals should be used for std::print. */
835+
#undef _GLIBCXX_USE_GLIBC_STDIO_EXT
836+
831837
/* Define if init_priority should be used for iostream initialization. */
832838
#undef _GLIBCXX_USE_INIT_PRIORITY_ATTRIBUTE
833839

@@ -893,6 +899,9 @@
893899
/* Define if sendfile is available in <sys/sendfile.h>. */
894900
#undef _GLIBCXX_USE_SENDFILE
895901

902+
/* Define if flockfile and putc_unlocked should be used for std::print. */
903+
#undef _GLIBCXX_USE_STDIO_LOCKING
904+
896905
/* Define to restrict std::__basic_file<> to stdio APIs. */
897906
#undef _GLIBCXX_USE_STDIO_PURE
898907

libstdc++-v3/configure

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54949,6 +54949,154 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
5494954949

5495054950

5495154951

54952+
# For std::print
54953+
54954+
54955+
ac_ext=cpp
54956+
ac_cpp='$CXXCPP $CPPFLAGS'
54957+
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
54958+
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
54959+
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
54960+
54961+
54962+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether flockfile and putc_unlocked are defined in <stdio.h>" >&5
54963+
$as_echo_n "checking whether flockfile and putc_unlocked are defined in <stdio.h>... " >&6; }
54964+
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
54965+
/* end confdefs.h. */
54966+
54967+
#include <stdio.h>
54968+
54969+
int
54970+
main ()
54971+
{
54972+
54973+
FILE* f = ::fopen("", "");
54974+
::flockfile(f);
54975+
::putc_unlocked(' ', f);
54976+
::funlockfile(f);
54977+
::fclose(f);
54978+
54979+
;
54980+
return 0;
54981+
}
54982+
_ACEOF
54983+
if ac_fn_cxx_try_compile "$LINENO"; then :
54984+
ac_stdio_locking=yes
54985+
else
54986+
ac_stdio_locking=no
54987+
fi
54988+
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
54989+
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_stdio_locking" >&5
54990+
$as_echo "$ac_stdio_locking" >&6; }
54991+
54992+
if test "$ac_stdio_locking" = yes; then
54993+
54994+
cat >>confdefs.h <<_ACEOF
54995+
#define _GLIBCXX_USE_STDIO_LOCKING 1
54996+
_ACEOF
54997+
54998+
54999+
# This is not defined in POSIX, but is present in glibc, musl, and Solaris.
55000+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether fwrite_unlocked is defined in <stdio.h>" >&5
55001+
$as_echo_n "checking whether fwrite_unlocked is defined in <stdio.h>... " >&6; }
55002+
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
55003+
/* end confdefs.h. */
55004+
55005+
#include <stdio.h>
55006+
55007+
int
55008+
main ()
55009+
{
55010+
55011+
FILE* f = ::fopen("", "");
55012+
::flockfile(f);
55013+
::fwrite_unlocked("", 1, 1, f);
55014+
::funlockfile(f);
55015+
::fclose(f);
55016+
55017+
;
55018+
return 0;
55019+
}
55020+
_ACEOF
55021+
if ac_fn_cxx_try_compile "$LINENO"; then :
55022+
ac_fwrite_unlocked=yes
55023+
else
55024+
ac_fwrite_unlocked=no
55025+
fi
55026+
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
55027+
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_fwrite_unlocked" >&5
55028+
$as_echo "$ac_fwrite_unlocked" >&6; }
55029+
if test "$ac_fwrite_unlocked" = yes; then
55030+
55031+
$as_echo "#define HAVE_FWRITE_UNLOCKED 1" >>confdefs.h
55032+
55033+
55034+
# Check for Glibc-specific FILE members and <stdio_ext.h> extensions.
55035+
case "${target_os}" in
55036+
gnu* | linux* | kfreebsd*-gnu | knetbsd*-gnu)
55037+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for FILE::_IO_write_ptr and <stdio_ext.h>" >&5
55038+
$as_echo_n "checking for FILE::_IO_write_ptr and <stdio_ext.h>... " >&6; }
55039+
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
55040+
/* end confdefs.h. */
55041+
55042+
#include <stdio.h>
55043+
#include <stdio_ext.h>
55044+
extern "C" {
55045+
using f1_type = int (*)(FILE*) noexcept;
55046+
using f2_type = size_t (*)(FILE*) noexcept;
55047+
}
55048+
55049+
int
55050+
main ()
55051+
{
55052+
55053+
f1_type twritable = &::__fwritable;
55054+
f1_type tblk = &::__flbf;
55055+
f2_type pbufsize = &::__fbufsize;
55056+
FILE* f = ::fopen("", "");
55057+
int i = ::__overflow(f, EOF);
55058+
bool writeable = ::__fwritable(f);
55059+
bool line_buffered = ::__flbf(f);
55060+
size_t bufsz = ::__fbufsize(f);
55061+
char*& pptr = f->_IO_write_ptr;
55062+
char*& epptr = f->_IO_buf_end;
55063+
::fflush_unlocked(f);
55064+
::fclose(f);
55065+
55066+
;
55067+
return 0;
55068+
}
55069+
_ACEOF
55070+
if ac_fn_cxx_try_compile "$LINENO"; then :
55071+
ac_glibc_stdio=yes
55072+
else
55073+
ac_glibc_stdio=no
55074+
fi
55075+
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
55076+
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_glibc_stdio" >&5
55077+
$as_echo "$ac_glibc_stdio" >&6; }
55078+
if test "$ac_glibc_stdio" = yes; then
55079+
55080+
cat >>confdefs.h <<_ACEOF
55081+
#define _GLIBCXX_USE_GLIBC_STDIO_EXT 1
55082+
_ACEOF
55083+
55084+
fi
55085+
;;
55086+
*)
55087+
;;
55088+
esac
55089+
fi
55090+
fi
55091+
55092+
ac_ext=c
55093+
ac_cpp='$CPP $CPPFLAGS'
55094+
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
55095+
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
55096+
ac_compiler_gnu=$ac_cv_c_compiler_gnu
55097+
55098+
55099+
5495255100
# Define documentation rules conditionally.
5495355101

5495455102
# See if makeinfo has been installed and is modern enough

libstdc++-v3/configure.ac

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,9 @@ GLIBCXX_CHECK_TEXT_ENCODING
590590
# For std::is_debugger_present
591591
GLIBCXX_CHECK_DEBUGGING
592592

593+
# For std::print
594+
GLIBCXX_CHECK_STDIO_LOCKING
595+
593596
# Define documentation rules conditionally.
594597

595598
# See if makeinfo has been installed and is modern enough

libstdc++-v3/include/bits/formatfwd.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,11 @@ namespace __format
190190
}();
191191
#endif // format_ranges
192192

193+
#if __glibcxx_print >= 202403L
194+
template<typename>
195+
constexpr bool enable_nonlocking_formatter_optimization = false;
196+
#endif
197+
193198
_GLIBCXX_END_NAMESPACE_VERSION
194199
} // namespace std
195200
#endif // __glibcxx_format

libstdc++-v3/include/bits/version.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1865,7 +1865,7 @@ ftms = {
18651865
ftms = {
18661866
name = print;
18671867
values = {
1868-
v = 202211;
1868+
v = 202403;
18691869
cxxmin = 23;
18701870
hosted = yes;
18711871
};

libstdc++-v3/include/bits/version.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2083,9 +2083,9 @@
20832083

20842084
#if !defined(__cpp_lib_print)
20852085
# if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED
2086-
# define __glibcxx_print 202211L
2086+
# define __glibcxx_print 202403L
20872087
# if defined(__glibcxx_want_all) || defined(__glibcxx_want_print)
2088-
# define __cpp_lib_print 202211L
2088+
# define __cpp_lib_print 202403L
20892089
# endif
20902090
# endif
20912091
#endif /* !defined(__cpp_lib_print) */

0 commit comments

Comments
 (0)