Skip to content

Commit 0ef5f85

Browse files
author
expnkx
committed
optimize performance of iobuf&streambuf_io_observer.h
The benchmark should be std::string_view not std::string. Or memcpy would be horribly slow
1 parent c8708be commit 0ef5f85

File tree

12 files changed

+188
-150
lines changed

12 files changed

+188
-150
lines changed

benchmarks/0014.raw_performance/file_io/c_file.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ int main()
55
{
66
constexpr std::size_t N{100000000};
77
fast_io::timer tm("c_file");
8-
std::string view("Hello World\n");
8+
constexpr std::string_view view("Hello World\n");
99
fast_io::c_file cfl("c_file.txt","wb");
1010
for(std::size_t i{};i!=N;++i)
1111
print(cfl,view);

benchmarks/0014.raw_performance/file_io/c_file_unlocked.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ int main()
55
{
66
constexpr std::size_t N{100000000};
77
fast_io::timer tm("c_file_unlocked");
8-
std::string view("Hello World\n");
8+
constexpr std::string_view view("Hello World\n");
99
fast_io::c_file_unlocked cfl("c_file_unlocked.txt","wb");
1010
for(std::size_t i{};i!=N;++i)
1111
print(cfl,view);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include"../../../include/fast_io.h"
2+
#include"../../../include/fast_io_legacy.h"
3+
#include"../../timer.h"
4+
5+
int main()
6+
{
7+
constexpr std::size_t N{100000000};
8+
{
9+
fast_io::timer tm("filebuf_file");
10+
fast_io::filebuf_file cfl("filebuf_file.txt","wb");
11+
constexpr std::string_view view("Hello World\n");
12+
for(std::size_t i{};i!=N;++i)
13+
print(cfl,view);
14+
}
15+
}

benchmarks/0014.raw_performance/file_io/fstream.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ int main()
66
{
77
constexpr std::size_t N{100000000};
88
fast_io::timer tm("fstream");
9-
std::string view("Hello World\n");
9+
constexpr std::string_view view("Hello World\n");
1010
std::ofstream fout("fstream.txt",std::ofstream::binary);
1111
auto& rdbuf(*fout.rdbuf());
1212
for(std::size_t i{};i!=N;++i)

benchmarks/0014.raw_performance/file_io/fwrite.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ int main()
55
{
66
constexpr std::size_t N{100000000};
77
fast_io::timer tm("fwrite");
8-
std::string view("Hello World\n");
8+
constexpr std::string_view view("Hello World\n");
99
fast_io::c_file cfl("fwrite.txt","wb");
1010
for(std::size_t i{};i!=N;++i)
1111
fwrite(view.data(),1,view.size(),cfl.native_handle());

benchmarks/0014.raw_performance/file_io/linux_bench_result.txt

-12
This file was deleted.

benchmarks/0014.raw_performance/file_io/obuf_file.cc

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
int main()
66
{
77
constexpr std::size_t N{100000000};
8+
{
89
fast_io::timer tm("obuf_file");
9-
std::string view("Hello World\n");
10+
constexpr std::string_view view("Hello World\n");
1011
fast_io::obuf_file obf("obuf_file.txt");
1112
for(std::size_t i{};i!=N;++i)
1213
print(obf,view);
14+
}
1315
}

benchmarks/0014.raw_performance/file_io/obuf_file_mutex.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ int main()
66
{
77
constexpr std::size_t N{100000000};
88
fast_io::timer tm("obuf_file mutex");
9-
std::string view("Hello World\n");
9+
constexpr std::string_view view("Hello World\n");
1010
fast_io::obuf_file_mutex obf("obuf_file_mutex.txt");
1111
for(std::size_t i{};i!=N;++i)
1212
print(obf,view);

benchmarks/0014.raw_performance/file_io/windows_bench_result.txt

-17
This file was deleted.

include/fast_io_freestanding_impl/iobuf.h

+113-78
Original file line numberDiff line numberDiff line change
@@ -184,59 +184,75 @@ concept write_read_punned_constraints = (std::contiguous_iterator<Iter>&&sizeof(
184184
namespace details
185185
{
186186
template<std::size_t buffer_size,bool punning=false,typename T,typename Iter>
187-
inline constexpr Iter ibuf_read(T& ib,Iter begin,Iter end)
187+
requires std::same_as<std::iter_value_t<Iter>,typename std::remove_cvref_t<T>::char_type>
188+
inline constexpr Iter ibuf_read_cold(T& ib,Iter begin,Iter end)
188189
{
189-
std::size_t const buffer_remain(ib.ibuffer.end-ib.ibuffer.curr);
190-
191190
std::size_t n(end-begin);
192-
if(buffer_remain<n) //cache miss
191+
std::size_t const buffer_remain(ib.ibuffer.end-ib.ibuffer.curr);
192+
if(ib.ibuffer.end==nullptr)
193193
{
194-
if(ib.ibuffer.end==nullptr)
194+
if(buffer_size<=n)
195195
{
196-
if(buffer_size<=n)
197-
{
198-
return read(ib.native_handle(),begin,end);
199-
}
200-
ib.ibuffer.init_space();
201-
ib.ibuffer.curr=ib.ibuffer.end=ib.ibuffer.beg;
196+
return read(ib.native_handle(),begin,end);
202197
}
203-
if constexpr(punning)
204-
{
205-
std::memcpy(begin,ib.ibuffer.curr,buffer_remain);
206-
begin+=buffer_remain;
207-
}
208-
else
209-
begin=std::copy_n(ib.ibuffer.curr,buffer_remain,begin);
210-
if(begin+buffer_size<end)
211-
{
198+
ib.ibuffer.init_space();
199+
ib.ibuffer.curr=ib.ibuffer.end=ib.ibuffer.beg;
200+
}
201+
if constexpr(punning)
202+
{
203+
std::memcpy(begin,ib.ibuffer.curr,buffer_remain*sizeof(std::iter_value_t<Iter>));
204+
begin+=buffer_remain;
205+
}
206+
else
207+
begin=std::copy_n(ib.ibuffer.curr,buffer_remain,begin);
208+
if(begin+buffer_size<end)
209+
{
212210
// if constexpr(std::contiguous_iterator<Iter>)
213-
begin=read(ib.native_handle(),begin,end);
211+
begin=read(ib.native_handle(),begin,end);
214212
/* else
215-
{
216-
217-
}*/
218-
if(begin!=end)
219-
{
220-
ib.ibuffer.end=ib.ibuffer.curr=ib.ibuffer.beg;
221-
return begin;
222-
}
213+
{
214+
215+
}*/
216+
if(begin!=end)
217+
{
218+
ib.ibuffer.end=ib.ibuffer.curr=ib.ibuffer.beg;
219+
return begin;
223220
}
224-
ib.ibuffer.end=read(ib.native_handle(),ib.ibuffer.beg,ib.ibuffer.beg+buffer_size);
225-
ib.ibuffer.curr=ib.ibuffer.beg;
226-
n=end-begin;
227-
std::size_t const sz(ib.ibuffer.end-ib.ibuffer.beg);
228-
if(sz<n)
229-
n=sz;
230221
}
222+
ib.ibuffer.end=read(ib.native_handle(),ib.ibuffer.beg,ib.ibuffer.beg+buffer_size);
223+
ib.ibuffer.curr=ib.ibuffer.beg;
224+
n=end-begin;
225+
std::size_t const sz(ib.ibuffer.end-ib.ibuffer.beg);
226+
if(sz<n)
227+
n=sz;
228+
if constexpr(punning)
229+
{
230+
std::memcpy(begin,ib.ibuffer.curr,n*sizeof(std::iter_value_t<Iter>));
231+
begin+=n;
232+
}
233+
else
234+
begin=std::copy_n(ib.ibuffer.curr,n,begin);
235+
ib.ibuffer.curr+=n;
236+
return begin;
237+
}
238+
239+
template<std::size_t buffer_size,bool punning=false,typename T,typename Iter>
240+
requires std::same_as<std::iter_value_t<Iter>,typename std::remove_cvref_t<T>::char_type>
241+
inline constexpr Iter ibuf_read(T& ib,Iter begin,Iter end)
242+
{
243+
std::size_t n(end-begin);
244+
if(ib.ibuffer.curr+n<ib.ibuffer.end)[[unlikely]] //cache miss
245+
return ibuf_read_cold<buffer_size,punning>(ib,begin,end);
231246
if constexpr(punning)
232247
{
233-
std::memcpy(begin,ib.ibuffer.curr,n);
248+
std::memcpy(begin,ib.ibuffer.curr,n*sizeof(std::iter_value_t<Iter>));
234249
begin+=n;
235250
}
236251
else
237252
begin=std::copy_n(ib.ibuffer.curr,n,begin);
238253
ib.ibuffer.curr+=n;
239254
return begin;
255+
240256
}
241257
}
242258

@@ -246,7 +262,12 @@ inline constexpr Iter read(basic_ibuf<Ihandler,Buf>& ib,Iter begin,Iter end)
246262
{
247263
using char_type = typename basic_ibuf<Ihandler,Buf>::char_type;
248264
if constexpr(std::same_as<char_type,typename std::iterator_traits<Iter>::value_type>)
249-
return details::ibuf_read<Buf::size>(ib,begin,end);
265+
{
266+
if(std::is_constant_evaluated())
267+
return details::ibuf_read<Buf::size>(ib,std::to_address(begin),std::to_address(end));
268+
else
269+
return details::ibuf_read<Buf::size,true>(ib,std::to_address(begin),std::to_address(end));
270+
}
250271
else
251272
{
252273
auto b(reinterpret_cast<char const*>(std::to_address(begin)));
@@ -408,56 +429,65 @@ inline constexpr decltype(auto) ibuffer_set_curr(basic_obuf<Ohandler,Buf>& ob,ty
408429
namespace details
409430
{
410431

411-
template<bool punning=false,typename T,typename Iter>
412-
inline constexpr void obuf_write(T& ob,Iter cbegin,Iter cend)
432+
template<bool punning=false,typename T,std::contiguous_iterator Iter>
433+
constexpr void obuf_write_cold(T& ob,Iter cbegin,Iter cend,std::size_t diff)
413434
{
414-
std::size_t const n(ob.obuffer.end-ob.obuffer.curr);
415-
std::size_t const diff(std::distance(cbegin,cend));
416-
if(n<diff)[[unlikely]]
435+
if(ob.obuffer.end==nullptr) //cold buffer
417436
{
418-
if(ob.obuffer.end==nullptr) //cold buffer
419-
{
420-
if(T::buffer_type::size<=diff)
421-
{
422-
write(ob.native_handle(),cbegin,cend);
423-
return;
424-
}
425-
ob.obuffer.init_space();
426-
ob.obuffer.end=(ob.obuffer.curr=ob.obuffer.beg)+T::buffer_type::size;
427-
if constexpr(punning)
428-
std::memcpy(ob.obuffer.curr,cbegin,diff);
429-
else
430-
std::copy_n(cbegin,diff,ob.obuffer.curr);
431-
ob.obuffer.curr+=diff;
432-
return;
433-
}
434-
/* if constexpr(punning)
435-
std::memcpy(ob.obuffer.curr,cbegin,n);
436-
else*/
437-
std::copy_n(cbegin,n,ob.obuffer.curr);
438-
cbegin+=n;
439-
write(ob.native_handle(),ob.obuffer.beg,ob.obuffer.end);
440-
if(cbegin+(T::buffer_type::size)<cend)
437+
if(T::buffer_type::size<=diff)
441438
{
442439
write(ob.native_handle(),cbegin,cend);
443-
ob.obuffer.curr=ob.obuffer.beg;
440+
return;
444441
}
442+
ob.obuffer.init_space();
443+
ob.obuffer.end=(ob.obuffer.curr=ob.obuffer.beg)+T::buffer_type::size;
444+
if constexpr(punning)
445+
memcpy(ob.obuffer.curr,cbegin,diff*sizeof(std::iter_value_t<Iter>));
445446
else
446-
{
447-
std::size_t const df(cend-cbegin);
448-
if constexpr(punning)
449-
std::memcpy(ob.obuffer.beg,cbegin,df);
450-
else
451-
std::copy_n(cbegin,df,ob.obuffer.beg);
452-
ob.obuffer.curr=ob.obuffer.beg+df;
453-
}
447+
std::copy_n(cbegin,diff,ob.obuffer.curr);
448+
ob.obuffer.curr+=diff;
454449
return;
455450
}
451+
std::size_t n(ob.obuffer.end-ob.obuffer.curr);
456452
if constexpr(punning)
457-
std::memcpy(ob.obuffer.curr,cbegin,diff);
453+
memcpy(ob.obuffer.curr,cbegin,n*sizeof(std::iter_value_t<Iter>));
454+
else
455+
std::copy_n(cbegin,n,ob.obuffer.curr);
456+
cbegin+=n;
457+
write(ob.native_handle(),ob.obuffer.beg,ob.obuffer.end);
458+
if(cbegin+(T::buffer_type::size)<cend)
459+
{
460+
write(ob.native_handle(),cbegin,cend);
461+
ob.obuffer.curr=ob.obuffer.beg;
462+
}
458463
else
459-
std::copy_n(cbegin,diff,ob.obuffer.curr);
460-
ob.obuffer.curr+=diff;
464+
{
465+
std::size_t const df(cend-cbegin);
466+
if constexpr(punning)
467+
memcpy(ob.obuffer.beg,cbegin,df*sizeof(std::iter_value_t<Iter>));
468+
else
469+
std::copy_n(cbegin,df,ob.obuffer.beg);
470+
ob.obuffer.curr=ob.obuffer.beg+df;
471+
}
472+
}
473+
474+
template<bool punning=false,typename T,std::contiguous_iterator Iter>
475+
requires std::same_as<std::iter_value_t<Iter>,typename std::remove_cvref_t<T>::char_type>
476+
inline constexpr void obuf_write(T& ob,Iter cbegin,Iter cend)
477+
{
478+
std::size_t const diff(cend-cbegin);
479+
auto curr{ob.obuffer.curr};
480+
auto e{curr+diff};
481+
if(e<ob.obuffer.end)[[likely]]
482+
{
483+
if constexpr(punning)
484+
memcpy(curr,cbegin,diff*sizeof(std::iter_value_t<Iter>));
485+
else
486+
std::copy_n(cbegin,diff,ob.obuffer.curr);
487+
ob.obuffer.curr=e;
488+
return;
489+
}
490+
obuf_write_cold<punning>(ob,cbegin,cend,diff);
461491
}
462492

463493
}
@@ -468,7 +498,12 @@ inline constexpr void write(basic_obuf<Ohandler,Buf>& ob,Iter cbegini,Iter cendi
468498
{
469499
using char_type = typename basic_obuf<Ohandler,Buf>::char_type;
470500
if constexpr(std::same_as<char_type,typename std::iterator_traits<Iter>::value_type>)
471-
details::obuf_write<false>(ob,std::to_address(cbegini),std::to_address(cendi));
501+
{
502+
if(std::is_constant_evaluated())
503+
details::obuf_write<false>(ob,std::to_address(cbegini),std::to_address(cendi));
504+
else
505+
details::obuf_write<true>(ob,std::to_address(cbegini),std::to_address(cendi));
506+
}
472507
else
473508
details::obuf_write<true>(ob,reinterpret_cast<char const*>(std::to_address(cbegini)),
474509
reinterpret_cast<char const*>(std::to_address(cendi)));

0 commit comments

Comments
 (0)