1- // Copyright 2019 Tencent
1+ // Copyright 2026 Tencent
22// SPDX-License-Identifier: BSD-3-Clause
33
44#include " flatten_x86.h"
1010#endif
1111#endif // __SSE2__
1212
13+ #include " cpu.h"
1314#include " x86_usability.h"
1415
1516namespace ncnn {
@@ -19,6 +20,10 @@ Flatten_x86::Flatten_x86()
1920#if __SSE2__
2021 support_packing = true ;
2122#endif // __SSE2__
23+ support_fp16_storage = cpu_support_x86_f16c ();
24+ #if NCNN_BF16
25+ support_bf16_storage = true ;
26+ #endif
2227}
2328
2429int Flatten_x86::forward (const Mat& bottom_blob, Mat& top_blob, const Option& opt) const
@@ -28,6 +33,9 @@ int Flatten_x86::forward(const Mat& bottom_blob, Mat& top_blob, const Option& op
2833 if (elembits == 8 )
2934 return forward_int8 (bottom_blob, top_blob, opt);
3035
36+ if (elembits == 16 )
37+ return forward_bf16s_fp16s (bottom_blob, top_blob, opt);
38+
3139 int dims = bottom_blob.dims ;
3240
3341 if (dims == 1 )
@@ -552,6 +560,319 @@ int Flatten_x86::forward(const Mat& bottom_blob, Mat& top_blob, const Option& op
552560 return 0 ;
553561}
554562
563+ int Flatten_x86::forward_bf16s_fp16s (const Mat& bottom_blob, Mat& top_blob, const Option& opt) const
564+ {
565+ int dims = bottom_blob.dims ;
566+
567+ if (dims == 1 )
568+ {
569+ top_blob = bottom_blob;
570+ return 0 ;
571+ }
572+
573+ int w = bottom_blob.w ;
574+ int h = bottom_blob.h ;
575+ int d = bottom_blob.d ;
576+ int channels = bottom_blob.c ;
577+ size_t elemsize = bottom_blob.elemsize ;
578+ int elempack = bottom_blob.elempack ;
579+ int size = w * h * d;
580+
581+ int total = size * channels * elempack;
582+
583+ int out_elempack = 1 ;
584+ #if __SSE2__
585+ if (opt.use_packing_layout )
586+ {
587+ #if __AVX512F__
588+ out_elempack = total % 16 == 0 ? 16 : total % 8 == 0 ? 8 : total % 4 == 0 ? 4 : 1 ;
589+ #elif __AVX__
590+ out_elempack = total % 8 == 0 ? 8 : total % 4 == 0 ? 4 : 1 ;
591+ #else
592+ out_elempack = total % 4 == 0 ? 4 : 1 ;
593+ #endif
594+ }
595+ #endif // __SSE2__
596+ size_t out_elemsize = elemsize / elempack * out_elempack;
597+
598+ if (out_elempack == 1 )
599+ {
600+ return Flatten::forward (bottom_blob, top_blob, opt);
601+ }
602+
603+ if (dims == 2 && elempack == 1 ) // out_elempack == 4 || out_elempack == 8 || out_elempack == 16
604+ {
605+ top_blob = bottom_blob;
606+ top_blob.dims = 1 ;
607+ top_blob.w = total / out_elempack;
608+ top_blob.h = 1 ;
609+ top_blob.cstep = bottom_blob.cstep / out_elempack;
610+ top_blob.elemsize = out_elemsize;
611+ top_blob.elempack = out_elempack;
612+ return 0 ;
613+ }
614+
615+ top_blob.create (total / out_elempack, out_elemsize, out_elempack, opt.blob_allocator );
616+ if (top_blob.empty ())
617+ return -100 ;
618+
619+ if (dims == 2 )
620+ {
621+ #if __SSE2__
622+ #if __AVX__
623+ #if __AVX512F__
624+ if (elempack == 16 ) // out_elempack == 16
625+ {
626+ #pragma omp parallel for num_threads(opt.num_threads)
627+ for (int i = 0 ; i < h; i++)
628+ {
629+ const unsigned short * ptr = bottom_blob.row <const unsigned short >(i);
630+ unsigned short * outptr0 = (unsigned short *)top_blob + w * i * 16 ;
631+ unsigned short * outptr1 = (unsigned short *)top_blob + w * (i * 16 + 1 );
632+ unsigned short * outptr2 = (unsigned short *)top_blob + w * (i * 16 + 2 );
633+ unsigned short * outptr3 = (unsigned short *)top_blob + w * (i * 16 + 3 );
634+ unsigned short * outptr4 = (unsigned short *)top_blob + w * (i * 16 + 4 );
635+ unsigned short * outptr5 = (unsigned short *)top_blob + w * (i * 16 + 5 );
636+ unsigned short * outptr6 = (unsigned short *)top_blob + w * (i * 16 + 6 );
637+ unsigned short * outptr7 = (unsigned short *)top_blob + w * (i * 16 + 7 );
638+ unsigned short * outptr8 = (unsigned short *)top_blob + w * (i * 16 + 8 );
639+ unsigned short * outptr9 = (unsigned short *)top_blob + w * (i * 16 + 9 );
640+ unsigned short * outptra = (unsigned short *)top_blob + w * (i * 16 + 10 );
641+ unsigned short * outptrb = (unsigned short *)top_blob + w * (i * 16 + 11 );
642+ unsigned short * outptrc = (unsigned short *)top_blob + w * (i * 16 + 12 );
643+ unsigned short * outptrd = (unsigned short *)top_blob + w * (i * 16 + 13 );
644+ unsigned short * outptre = (unsigned short *)top_blob + w * (i * 16 + 14 );
645+ unsigned short * outptrf = (unsigned short *)top_blob + w * (i * 16 + 15 );
646+
647+ for (int j = 0 ; j < w; j++)
648+ {
649+ *outptr0++ = ptr[0 ];
650+ *outptr1++ = ptr[1 ];
651+ *outptr2++ = ptr[2 ];
652+ *outptr3++ = ptr[3 ];
653+ *outptr4++ = ptr[4 ];
654+ *outptr5++ = ptr[5 ];
655+ *outptr6++ = ptr[6 ];
656+ *outptr7++ = ptr[7 ];
657+ *outptr8++ = ptr[8 ];
658+ *outptr9++ = ptr[9 ];
659+ *outptra++ = ptr[10 ];
660+ *outptrb++ = ptr[11 ];
661+ *outptrc++ = ptr[12 ];
662+ *outptrd++ = ptr[13 ];
663+ *outptre++ = ptr[14 ];
664+ *outptrf++ = ptr[15 ];
665+
666+ ptr += 16 ;
667+ }
668+ }
669+ }
670+ #endif // __AVX512F__
671+
672+ if (elempack == 8 ) // out_elempack == 8 || out_elempack == 16
673+ {
674+ #pragma omp parallel for num_threads(opt.num_threads)
675+ for (int i = 0 ; i < h; i++)
676+ {
677+ const unsigned short * ptr = bottom_blob.row <const unsigned short >(i);
678+ unsigned short * outptr0 = (unsigned short *)top_blob + w * i * 8 ;
679+ unsigned short * outptr1 = (unsigned short *)top_blob + w * (i * 8 + 1 );
680+ unsigned short * outptr2 = (unsigned short *)top_blob + w * (i * 8 + 2 );
681+ unsigned short * outptr3 = (unsigned short *)top_blob + w * (i * 8 + 3 );
682+ unsigned short * outptr4 = (unsigned short *)top_blob + w * (i * 8 + 4 );
683+ unsigned short * outptr5 = (unsigned short *)top_blob + w * (i * 8 + 5 );
684+ unsigned short * outptr6 = (unsigned short *)top_blob + w * (i * 8 + 6 );
685+ unsigned short * outptr7 = (unsigned short *)top_blob + w * (i * 8 + 7 );
686+
687+ for (int j = 0 ; j < w; j++)
688+ {
689+ *outptr0++ = ptr[0 ];
690+ *outptr1++ = ptr[1 ];
691+ *outptr2++ = ptr[2 ];
692+ *outptr3++ = ptr[3 ];
693+ *outptr4++ = ptr[4 ];
694+ *outptr5++ = ptr[5 ];
695+ *outptr6++ = ptr[6 ];
696+ *outptr7++ = ptr[7 ];
697+
698+ ptr += 8 ;
699+ }
700+ }
701+ }
702+ #endif // __AVX__
703+
704+ if (elempack == 4 ) // out_elempack == 4 || out_elempack == 8 || out_elempack == 16
705+ {
706+ #pragma omp parallel for num_threads(opt.num_threads)
707+ for (int i = 0 ; i < h; i++)
708+ {
709+ const unsigned short * ptr = bottom_blob.row <const unsigned short >(i);
710+ unsigned short * outptr0 = (unsigned short *)top_blob + w * i * 4 ;
711+ unsigned short * outptr1 = (unsigned short *)top_blob + w * (i * 4 + 1 );
712+ unsigned short * outptr2 = (unsigned short *)top_blob + w * (i * 4 + 2 );
713+ unsigned short * outptr3 = (unsigned short *)top_blob + w * (i * 4 + 3 );
714+
715+ for (int j = 0 ; j < w; j++)
716+ {
717+ *outptr0++ = ptr[0 ];
718+ *outptr1++ = ptr[1 ];
719+ *outptr2++ = ptr[2 ];
720+ *outptr3++ = ptr[3 ];
721+
722+ ptr += 4 ;
723+ }
724+ }
725+ }
726+ #endif // __SSE2__
727+ }
728+
729+ if (dims == 3 || dims == 4 )
730+ {
731+ #if __SSE2__
732+ #if __AVX__
733+ #if __AVX512F__
734+ if (elempack == 16 ) // out_elempack == 16
735+ {
736+ #pragma omp parallel for num_threads(opt.num_threads)
737+ for (int q = 0 ; q < channels; q++)
738+ {
739+ const unsigned short * ptr = bottom_blob.channel (q);
740+ unsigned short * outptr0 = (unsigned short *)top_blob + size * q * 16 ;
741+ unsigned short * outptr1 = (unsigned short *)top_blob + size * (q * 16 + 1 );
742+ unsigned short * outptr2 = (unsigned short *)top_blob + size * (q * 16 + 2 );
743+ unsigned short * outptr3 = (unsigned short *)top_blob + size * (q * 16 + 3 );
744+ unsigned short * outptr4 = (unsigned short *)top_blob + size * (q * 16 + 4 );
745+ unsigned short * outptr5 = (unsigned short *)top_blob + size * (q * 16 + 5 );
746+ unsigned short * outptr6 = (unsigned short *)top_blob + size * (q * 16 + 6 );
747+ unsigned short * outptr7 = (unsigned short *)top_blob + size * (q * 16 + 7 );
748+ unsigned short * outptr8 = (unsigned short *)top_blob + size * (q * 16 + 8 );
749+ unsigned short * outptr9 = (unsigned short *)top_blob + size * (q * 16 + 9 );
750+ unsigned short * outptra = (unsigned short *)top_blob + size * (q * 16 + 10 );
751+ unsigned short * outptrb = (unsigned short *)top_blob + size * (q * 16 + 11 );
752+ unsigned short * outptrc = (unsigned short *)top_blob + size * (q * 16 + 12 );
753+ unsigned short * outptrd = (unsigned short *)top_blob + size * (q * 16 + 13 );
754+ unsigned short * outptre = (unsigned short *)top_blob + size * (q * 16 + 14 );
755+ unsigned short * outptrf = (unsigned short *)top_blob + size * (q * 16 + 15 );
756+
757+ for (int i = 0 ; i < size; i++)
758+ {
759+ *outptr0++ = ptr[0 ];
760+ *outptr1++ = ptr[1 ];
761+ *outptr2++ = ptr[2 ];
762+ *outptr3++ = ptr[3 ];
763+ *outptr4++ = ptr[4 ];
764+ *outptr5++ = ptr[5 ];
765+ *outptr6++ = ptr[6 ];
766+ *outptr7++ = ptr[7 ];
767+ *outptr8++ = ptr[8 ];
768+ *outptr9++ = ptr[9 ];
769+ *outptra++ = ptr[10 ];
770+ *outptrb++ = ptr[11 ];
771+ *outptrc++ = ptr[12 ];
772+ *outptrd++ = ptr[13 ];
773+ *outptre++ = ptr[14 ];
774+ *outptrf++ = ptr[15 ];
775+
776+ ptr += 16 ;
777+ }
778+ }
779+ }
780+ #endif // __AVX512F__
781+
782+ if (elempack == 8 ) // out_elempack == 8 || out_elempack == 16
783+ {
784+ #pragma omp parallel for num_threads(opt.num_threads)
785+ for (int q = 0 ; q < channels; q++)
786+ {
787+ const unsigned short * ptr = bottom_blob.channel (q);
788+ unsigned short * outptr0 = (unsigned short *)top_blob + size * q * 8 ;
789+ unsigned short * outptr1 = (unsigned short *)top_blob + size * (q * 8 + 1 );
790+ unsigned short * outptr2 = (unsigned short *)top_blob + size * (q * 8 + 2 );
791+ unsigned short * outptr3 = (unsigned short *)top_blob + size * (q * 8 + 3 );
792+ unsigned short * outptr4 = (unsigned short *)top_blob + size * (q * 8 + 4 );
793+ unsigned short * outptr5 = (unsigned short *)top_blob + size * (q * 8 + 5 );
794+ unsigned short * outptr6 = (unsigned short *)top_blob + size * (q * 8 + 6 );
795+ unsigned short * outptr7 = (unsigned short *)top_blob + size * (q * 8 + 7 );
796+
797+ for (int i = 0 ; i < size; i++)
798+ {
799+ *outptr0++ = ptr[0 ];
800+ *outptr1++ = ptr[1 ];
801+ *outptr2++ = ptr[2 ];
802+ *outptr3++ = ptr[3 ];
803+ *outptr4++ = ptr[4 ];
804+ *outptr5++ = ptr[5 ];
805+ *outptr6++ = ptr[6 ];
806+ *outptr7++ = ptr[7 ];
807+
808+ ptr += 8 ;
809+ }
810+ }
811+ }
812+ #endif // __AVX__
813+
814+ if (elempack == 4 ) // out_elempack == 4 || out_elempack == 8 || out_elempack == 16
815+ {
816+ #pragma omp parallel for num_threads(opt.num_threads)
817+ for (int q = 0 ; q < channels; q++)
818+ {
819+ const unsigned short * ptr = bottom_blob.channel (q);
820+ unsigned short * outptr0 = (unsigned short *)top_blob + size * q * 4 ;
821+ unsigned short * outptr1 = (unsigned short *)top_blob + size * (q * 4 + 1 );
822+ unsigned short * outptr2 = (unsigned short *)top_blob + size * (q * 4 + 2 );
823+ unsigned short * outptr3 = (unsigned short *)top_blob + size * (q * 4 + 3 );
824+
825+ for (int i = 0 ; i < size; i++)
826+ {
827+ *outptr0++ = ptr[0 ];
828+ *outptr1++ = ptr[1 ];
829+ *outptr2++ = ptr[2 ];
830+ *outptr3++ = ptr[3 ];
831+
832+ ptr += 4 ;
833+ }
834+ }
835+ }
836+ #endif // __SSE2__
837+
838+ if (elempack == 1 ) // out_elempack == 4 || out_elempack == 8 || out_elempack == 16
839+ {
840+ #pragma omp parallel for num_threads(opt.num_threads)
841+ for (int q = 0 ; q < channels; q++)
842+ {
843+ const unsigned short * ptr = bottom_blob.channel (q);
844+ unsigned short * outptr = (unsigned short *)top_blob + size * q;
845+
846+ int i = 0 ;
847+ #if __SSE2__
848+ #if __AVX__
849+ for (; i + 15 < size; i += 16 )
850+ {
851+ __m256i _v = _mm256_loadu_si256 ((const __m256i*)ptr);
852+ _mm256_storeu_si256 ((__m256i*)outptr, _v);
853+ ptr += 16 ;
854+ outptr += 16 ;
855+ }
856+ #endif
857+ for (; i + 7 < size; i += 8 )
858+ {
859+ __m128i _v = _mm_loadu_si128 ((const __m128i*)ptr);
860+ _mm_storeu_si128 ((__m128i*)outptr, _v);
861+ ptr += 8 ;
862+ outptr += 8 ;
863+ }
864+ #endif // __SSE2__
865+ for (; i < size; i++)
866+ {
867+ *outptr++ = *ptr++;
868+ }
869+ }
870+ }
871+ }
872+
873+ return 0 ;
874+ }
875+
555876int Flatten_x86::forward_int8 (const Mat& bottom_blob, Mat& top_blob, const Option& opt) const
556877{
557878 int dims = bottom_blob.dims ;
0 commit comments