SIMD рдХреЗ рд╕рд╛рде рдлреНрд▓реЛрдЯ 4x4 рдореИрдЯреНрд░рд┐рдХреНрд╕ рдЧреБрдгрди рдХреЛ рдЧрддрд┐ рджреЗрдВ

рдЗрдВрдЯреЗрд▓ рдкреНрд░реЛрд╕реЗрд╕рд░ рдкрд░ рдирд┐рд░реНрджреЗрд╢ рдПрдордПрдордПрдХреНрд╕, рдПрд╕рдПрд╕рдИ рдФрд░ рдмрд╛рдж рдореЗрдВ рдПрд╡реАрдПрдХреНрд╕ рд╕реЗ рдкрд░рд┐рдЪрд┐рдд рд╣реЛрдиреЗ рдХреЗ рдмрд╛рдж рдХрдИ рд╕рд╛рд▓ рдмреАрдд рдЪреБрдХреЗ рд╣реИрдВред рдПрдХ рд╕рдордп рдореЗрдВ, рд╡реЗ x86 рдЕрд╕реЗрдВрдмрд▓рд░ рдХреА рдкреГрд╖реНрдарднреВрдорд┐ рдХреЗ рдЦрд┐рд▓рд╛рдл рдХрд┐рд╕реА рддрд░рд╣ рдХреЗ рдЬрд╛рджреВ рдХреА рддрд░рд╣ рд▓рдЧ рд░рд╣реЗ рдереЗ, рдЬреЛ рд▓рдВрдмреЗ рд╕рдордп рд╕реЗ рдХреБрдЫ рд╕рд╛рдВрд╕рд╛рд░рд┐рдХ рдерд╛ред рдЙрдиреНрд╣реЛрдВрдиреЗ рдореБрдЭреЗ рдЗрддрдирд╛ рдЭреБрдХрд╛ рджрд┐рдпрд╛ рдХрд┐ рдХреБрдЫ рд╕рд╛рд▓ рдкрд╣рд▓реЗ рдореБрдЭреЗ рдПрдХ рдкреНрд░рд╕рд┐рджреНрдз рдЧреЗрдо рдХреЗ рд▓рд┐рдП рдЕрдкрдирд╛ рд╕реЙрдлреНрдЯрд╡реЗрдпрд░ рд░реЗрдВрдбрд░ рд▓рд┐рдЦрдиреЗ рдХрд╛ рд╡рд┐рдЪрд╛рд░ рдЖрдпрд╛ред рдЬрд┐рд╕ рдЪреАрдЬ рдиреЗ рдЗрди рдирд┐рд░реНрджреЗрд╢реЛрдВ рдХрд╛ рд╡рд╛рджрд╛ рдХрд┐рдпрд╛ рдерд╛ рдЙрд╕рдиреЗ рдореБрдЭрд╕реЗ рдпрд╣ рд╡рд╛рджрд╛ рдХрд┐рдпрд╛ рдерд╛ред рдХреБрдЫ рдмрд┐рдВрджреБ рдкрд░, рдореИрдВрдиреЗ рдЗрд╕реЗ рд▓рд┐рдЦрдиреЗ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рднреА рд╕реЛрдЪрд╛ред рд▓реЗрдХрд┐рди рдкрд╛рда рд▓рд┐рдЦрдирд╛ рдХреЛрдб рдХреА рддреБрд▓рдирд╛ рдореЗрдВ рдмрд╣реБрдд рдЕрдзрд┐рдХ рдЬрдЯрд┐рд▓ рд╣реЛ рдЧрдпрд╛ред

рдЙрд╕ рд╕рдордп, рдореИрдВ рд╡рд┐рднрд┐рдиреНрди рдкреНрд░реЛрд╕реЗрд╕рд░ рдкрд░ рд╕рдорд░реНрдерди рдХреЗ рд╕рд╛рде рд╕рдорд╕реНрдпрд╛рдУрдВ рд╕реЗ рдмрдЪрдирд╛ рдЪрд╛рд╣рддрд╛ рдерд╛ред рдореИрдВ рдЕрдзрд┐рдХрддрдо рдЙрдкрд▓рдмреНрдз рд░рд╛рд╢рд┐ рдкрд░ рдЕрдкрдиреЗ рд░реЗрдВрдбрд░рд░ рдХреА рдЬрд╛рдВрдЪ рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реЛрдирд╛ рдЪрд╛рд╣рддрд╛ рдерд╛ред рдореЗрд░реЗ рдкрд╛рд╕ рдЕрднреА рднреА рдкреБрд░рд╛рдиреЗ AMD рдкреНрд░реЛрд╕реЗрд╕рд░ рд╡рд╛рд▓реЗ рджреЛрд╕реНрдд рд╣реИрдВ, рдФрд░ рдЙрдирдХреА рдЫрдд SSE3 рдереАред рдЗрд╕рд▓рд┐рдП, рдЙрд╕ рд╕рдордп рдореИрдВрдиреЗ рдЦреБрдж рдХреЛ рдЕрдзрд┐рдХрддрдо SSE3 рддрдХ рд╕реАрдорд┐рдд рд░рдЦрдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛ред рддреЛ рдПрдХ рд╡реЗрдХреНрдЯрд░ рдЧрдгрд┐рддреАрдп рдкреБрд╕реНрддрдХрд╛рд▓рдп рдерд╛, рдЬреЛ SSE3 рдХреЗ рдкрд╣рд▓реЗ рдПрдХ рджреБрд░реНрд▓рдн рд╕рдорд╛рд╡реЗрд╢ рдХреЗ рд╕рд╛рде, SSE рдкрд░ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рд▓рд╛рдЧреВ рд╣реЛрдиреЗ рд╕реЗ рдереЛрдбрд╝рд╛ рдХрдо рдерд╛ред рд╣рд╛рд▓рд╛рдВрдХрд┐, рдХреБрдЫ рдмрд┐рдВрджреБ рдкрд░ рдореИрдВрдиреЗ рд╕реЛрдЪрд╛ рдХрд┐ рдореИрдВ рдХрд┐рддрдиреЗ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдкреНрд░рджрд░реНрд╢рди рдХрд░ рд╕рдХрддрд╛ рд╣реВрдВ рдЬреЛ рдХрд┐ рдХрдИ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╡реЗрдХреНрдЯрд░ рдЧрдгрд┐рдд рдХрд╛рд░реНрдпреЛрдВ рдХреЗ рд▓рд┐рдП рдкреНрд░реЛрд╕реЗрд╕рд░ рд╕реЗ рдмрд╛рд╣рд░ рдирд┐рдЪреЛрдбрд╝ рд╕рдХрддрд╛ рд╣реИред рдРрд╕рд╛ рд╣реА рдПрдХ рдСрдкрд░реЗрд╢рди 4 рдореИрдЯреНрд░рд┐рд╕реЗрд╕ рдХреЛ 4 рд╕реЗ рдЧреБрдгрд╛ рдХрд░рдирд╛ рд╣реИред


рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рдореИрдВрдиреЗ рдЗрд╕ рд╡реНрдпрд╡рд╕рд╛рдп рдХреЛ рдордиреЛрд░рдВрдЬрди рдХреЗ рд▓рд┐рдП рдЕрдзрд┐рдХ рдХрд░рдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛ред рдореИрдВрдиреЗ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА SSE рдкрд░ рдЕрдкрдиреЗ рд╕реЙрдлреНрдЯрд╡реЗрдпрд░ рдкреНрд░рддрд┐рдкрд╛рджрди рдХреЗ рд▓рд┐рдП рдореИрдЯреНрд░рд┐рдХреНрд╕ рдЧреБрдгрд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рд╣реИ рдФрд░ рд▓рд┐рдЦрд╛ рд╣реИ рдФрд░ рдпрд╣ рдореЗрд░реЗ рд▓рд┐рдП рдкрд░реНрдпрд╛рдкреНрдд рд▓рдЧрддрд╛ рд╣реИред рд▓реЗрдХрд┐рди рдлрд┐рд░ рдореИрдВрдиреЗ рдпрд╣ рджреЗрдЦрдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛ рдХрд┐ рдореИрдВ 2 рдлреНрд▓реЛрдЯ 4x4 рдореИрдЯреНрд░рд┐рд╕ рдХреЛ рдЧреБрдгрд╛ рдХрд░рдиреЗ рд╕реЗ рд╕рд┐рджреНрдзрд╛рдВрдд рдореЗрдВ рдХрд┐рддрдиреЗ рдЙрдкрд╛рдп рдХрд░ рд╕рдХрддрд╛ рд╣реВрдВред рдореЗрд░реЗ рд╡рд░реНрддрдорд╛рди SSE рдкрд░, рдпреЗ 16 рдШрдбрд╝реА рдЪрдХреНрд░ рд╣реИрдВред рд╕рдЪ рд╣реИ, IACA 3 рдХреЗ рд▓рд┐рдП рд╣рд╛рд▓ рд╣реА рдореЗрдВ рд╕рдВрдХреНрд░рдордг 19 рдХреЛ рджрд┐рдЦрд╛рдирд╛ рд╢реБрд░реВ рдХрд┐рдпрд╛, рдХреНрдпреЛрдВрдХрд┐ рдореИрдВрдиреЗ 0 * рдХреЗ рдмрдЬрд╛рдп рдХреБрдЫ рдирд┐рд░реНрджреЗрд╢реЛрдВ рдХреЗ рд▓рд┐рдП 1 * рд▓рд┐рдЦрдирд╛ рд╢реБрд░реВ рдХрд┐рдпрд╛ред рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдкрд╣рд▓реЗ рдпрд╣ рдХреЗрд╡рд▓ рд╡рд┐рд╢реНрд▓реЗрд╖рдХ рдореЗрдВ рдПрдХ рджреЛрд╖ рдерд╛ред

рдЙрдкрдпреЛрдЧ рдХреА рдЬрд╛рдиреЗ рд╡рд╛рд▓реА рдЙрдкрдпреЛрдЧрд┐рддрд╛рдУрдВ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВ


рдХреЛрдб рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХреЗ рд▓рд┐рдП рдореИрдВрдиреЗ рдкреНрд░рд╕рд┐рджреНрдз рдЗрдВрдЯреЗрд▓ рдЖрд░реНрдХрд┐рдЯреЗрдХреНрдЪрд░ рдХреЛрдб рд╡рд┐рд╢реНрд▓реЗрд╖рдХ рдЙрдкрдпреЛрдЧрд┐рддрд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ред рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХреЗ рд▓рд┐рдП рдореИрдВ рдПрд╡реАрдПрдХреНрд╕ 2 рдХреЗ рд╕рдорд░реНрдерди рдХреЗ рд╕рд╛рде рдиреНрдпреВрдирддрдо рдХреЗ рд░реВрдк рдореЗрдВ, рд╣реИрд╕рд╡реЗрд▓ рд╡рд╛рд╕реНрддреБрдХрд▓рд╛ (рдПрдЪрдПрд╕рдбрдмреНрд▓реНрдпреВ) рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реВрдВред рдХреЛрдб рд▓рд┐рдЦрдирд╛ рднреА рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдмрд╣реБрдд рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд╣реИ: рдЗрдВрдЯреЗрд▓ рдЖрдВрддрд░рд┐рдХ рдЧрд╛рдЗрдб рдФрд░ рдЗрдВрдЯреЗрд▓ рдЕрдиреБрдХреВрд▓рди рдореИрдиреБрдЕрд▓ ред

рдЕрд╕реЗрдВрдмрд▓реА рдХреЗ рд▓рд┐рдП рдореИрдВ рдХрдВрд╕реЛрд▓ рд╕реЗ MSVS 2017 рд╕рдореБрджрд╛рдп рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реВрдВред рдореИрдВ рд╕рдВрд╕реНрдХрд░рдг рдореЗрдВ рдЖрдВрддрд░рд┐рдХ рдХреЗ рд╕рд╛рде рдХреЛрдб рд▓рд┐рдЦрддрд╛ рд╣реВрдВред рдЖрдк рдПрдХ рдмрд╛рд░ рд▓рд┐рдЦрддреЗ рд╣реИрдВ, рдФрд░ рдЖрдорддреМрд░ рдкрд░ рдпрд╣ рддреБрд░рдВрдд рд╡рд┐рднрд┐рдиреНрди рдкреНрд▓реЗрдЯрдлрд╛рд░реНрдореЛрдВ рдкрд░ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, x64 VC ++ рдХрдВрдкрд╛рдЗрд▓рд░ рдЗрдирд▓рд╛рдЗрди рдЕрд╕реЗрдВрдмрд▓рд░ рдХрд╛ рд╕рдорд░реНрдерди рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдореИрдВ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ рдХрд┐ рдпрд╣ x64 рдХреЗ рддрд╣рдд рднреА рдХрд╛рдо рдХрд░реЗред

рдЪреВрдВрдХрд┐ рдпрд╣ рд▓реЗрдЦ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА SIMD рдкреНрд░реЛрдЧреНрд░рд╛рдорд┐рдВрдЧ рдореЗрдВ рд╢реБрд░реБрдЖрддреА рд╕реНрддрд░ рд╕реЗ рдкрд░реЗ рд╣реИ, рдореИрдВ рд╕реБрдВрджрд░ рдЪрд┐рддреНрд░реЛрдВ рдХреЗ рд░рдЬрд┐рд╕реНрдЯрд░реЛрдВ, рдирд┐рд░реНрджреЗрд╢реЛрдВ, рдбреНрд░рд╛ (рдпрд╛ рджрд╛рдврд╝реА) рдХрд╛ рд╡рд░реНрдгрди рдирд╣реАрдВ рдХрд░реВрдВрдЧрд╛ рдФрд░ SIMD рдирд┐рд░реНрджреЗрд╢реЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдкреНрд░реЛрдЧреНрд░рд╛рдорд┐рдВрдЧ рд╕реАрдЦрдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░реВрдВрдЧрд╛ред рдЗрдВрдЯреЗрд▓ рд╡реЗрдмрд╕рд╛рдЗрдЯ рдЙрддреНрдХреГрд╖реНрдЯ, рд╕реНрдкрд╖реНрдЯ рдФрд░ рд╡рд┐рд╕реНрддреГрдд рдкреНрд░рд▓реЗрдЦрди рд╕реЗ рднрд░реА рд╣реИред

рдореИрдВ рд╕рдм рдХреБрдЫ рдЖрд╕рд╛рди рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рдерд╛ред ... рд▓реЗрдХрд┐рди рдпрд╣ рд╣рдореЗрд╢рд╛ рдХреА рддрд░рд╣ рдирд┐рдХрд▓рд╛


рдпрд╣ рд╡рд╣ рдЬрдЧрд╣ рд╣реИ рдЬрд╣рд╛рдВ рдХреНрд╖рдг рд╢реБрд░реВ рд╣реЛрддрд╛ рд╣реИ, рдЬреЛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдФрд░ рд▓реЗрдЦ рджреЛрдиреЛрдВ рдХреЛ рдмрд╣реБрдд рдЕрдзрд┐рдХ рдЬрдЯрд┐рд▓ рдХрд░рддрд╛ рд╣реИред рдЗрд╕рд▓рд┐рдП, рдореИрдВ рдЗрд╕ рдкрд░ рдереЛрдбрд╝рд╛ рдзреНрдпрд╛рди рджреВрдВрдЧрд╛ред рддрддреНрд╡реЛрдВ рдХреА рдорд╛рдирдХ рдкрдВрдХреНрддрд┐ рд▓реЗрдЖрдЙрдЯ рдХреЗ рд╕рд╛рде рдореИрдЯреНрд░рд┐рдХреНрд╕ рдЧреБрдгрд╛ рд▓рд┐рдЦрдирд╛ рдореЗрд░реЗ рд▓рд┐рдП рджрд┐рд▓рдЪрд╕реНрдк рдирд╣реАрдВ рд╣реИред рдЬрд┐рдиреНрд╣реЗрдВ рдЗрд╕рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдереА, рдФрд░ рдЗрд╕рд▓рд┐рдП рдЙрдиреНрд╣реЛрдВрдиреЗ рд╡рд┐рд╢реНрд╡рд╡рд┐рджреНрдпрд╛рд▓рдпреЛрдВ рдореЗрдВ рдпрд╛ рдЕрдкрдиреЗ рджрдо рдкрд░ рдЕрдзреНрдпрдпрди рдХрд┐рдпрд╛ред рд╣рдорд╛рд░рд╛ рд▓рдХреНрд╖реНрдп рдЙрддреНрдкрд╛рджрдХрддрд╛ рд╣реИред рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рдореИрдВрдиреЗ рдмрд╣реБрдд рд╕рдордп рдкрд╣рд▓реЗ рдХреЙрд▓рдо рд▓реЗрдЖрдЙрдЯ рдкрд░ рд╕реНрд╡рд┐рдЪ рдХрд┐рдпрд╛ рдерд╛ред рдореЗрд░рд╛ рд╕реЙрдлрд╝реНрдЯрд╡реЗрдпрд░ рд░реЗрдВрдбрд░рд░ OpenGL API рдкрд░ рдЖрдзрд╛рд░рд┐рдд рд╣реИ рдФрд░ рдЗрд╕рд▓рд┐рдП, рдЕрдирд╛рд╡рд╢реНрдпрдХ рдЯреНрд░рд╛рдВрд╕рдкреЛрдЬрд╝рд┐рд╢рди рд╕реЗ рдмрдЪрдиреЗ рдХреЗ рд▓рд┐рдП, рдореИрдВрдиреЗ рдХреЙрд▓рдо рдореЗрдВ рддрддреНрд╡реЛрдВ рдХреЛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдирд╛ рд╢реБрд░реВ рдХрд░ рджрд┐рдпрд╛ред рдпрд╣ рднреА рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рдореИрдЯреНрд░рд┐рдХреНрд╕ рдЧреБрдгрди рдЗрддрдирд╛ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдирд╣реАрдВ рд╣реИред рдЕрдЪреНрдЫреА рддрд░рд╣ рд╕реЗ 2-5-10 рдЧреБрдгрд╛ рдЧреБрдгрд╛ред рдФрд░ рд╡рд╣ рдпрд╣ рд╣реИред рдФрд░ рдлрд┐рд░ рд╣рдо рддреИрдпрд╛рд░ рдореИрдЯреНрд░рд┐рдХреНрд╕ рдХреЛ рд╣рдЬрд╛рд░реЛрдВ рдпрд╛ рд▓рд╛рдЦреЛрдВ рдЪрдХреНрдХрд░реЛрдВ рд╕реЗ рдЧреБрдгрд╛ рдХрд░рддреЗ рд╣реИрдВред рдФрд░ рдпрд╣ рдСрдкрд░реЗрд╢рди рдЕрдзрд┐рдХ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИред рдЖрдк рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ, рд╣рд░ рдмрд╛рд░ рд╕рдВрдХреНрд░рдордг рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рд▓реЗрдХрд┐рди рдХреНрдпреЛрдВ, рдЕрдЧрд░ рдЗрд╕рд╕реЗ рдмрдЪрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред

рд▓реЗрдХрд┐рди рд╡рд┐рд╢реЗрд╖ рд░реВрдк рд╕реЗ рдореЗрдЯреНрд░рд┐рд╕реЗрд╕ рдкрд░ рд╡рд╛рдкрд╕ред рд╣рдордиреЗ рдХреЙрд▓рдо рдореЗрдВ рднрдВрдбрд╛рд░рдг рдХрд╛ рдирд┐рд░реНрдзрд╛рд░рдг рдХрд┐рдпрд╛ред рд╣рд╛рд▓рд╛рдБрдХрд┐, рдпрд╣ рдФрд░ рдЬрдЯрд┐рд▓ рд╣реЛ рд╕рдХрддрд╛ рд╣реИред SIMD рд░рдЬрд┐рд╕реНрдЯрд░реЛрдВ рдореЗрдВ рд╡реИрдХреНрдЯрд░ рдФрд░ рдореИрдЯреНрд░рд┐рдХреНрд╕ рдкрдВрдХреНрддрд┐рдпреЛрдВ рдХреЗ рд╡рд░рд┐рд╖реНрда рддрддреНрд╡реЛрдВ рдХреЛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдирд╛ рдореЗрд░реЗ рд▓рд┐рдП рдЕрдзрд┐рдХ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд╣реИ рддрд╛рдХрд┐ x рдЙрдЪреНрдЪрддрдо рдлреНрд▓реЛрдЯ (рдЕрдиреБрдХреНрд░рдордгрд┐рдХрд╛ 3) рдореЗрдВ рд╣реЛ, рдФрд░ рдорд╛рдЗрдирд░ (рдЕрдиреБрдХреНрд░рдордгрд┐рдХрд╛ 0) рдореЗрдВ w ред рдпрд╣рд╛рдБ, рдЬрд╛рд╣рд┐рд░ рд╣реИ, рд╣рдореЗрдВ рдлрд┐рд░ рд╕реЗ рдкреАрдЫреЗ рд╣рдЯрдирд╛ рд╣реЛрдЧрд╛ рдХреНрдпреЛрдВред

рддрдереНрдп рдпрд╣ рд╣реИ рдХрд┐ рдПрдХ рд╡реЗрдХреНрдЯрд░ рдореЗрдВ рдПрдХ рд╕реЙрдлреНрдЯрд╡реЗрдпрд░ рд░реЗрдВрдбрд░рд░ рдореЗрдВ, рдЖрдкрдХреЛ рдбрдмреНрд▓реНрдпреВ рдШрдЯрдХ рдХреЛ рдЕрдзрд┐рдХ рдмрд╛рд░ рд╣реЗрд░рдлреЗрд░ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ ( 1 / z рд╡рд╣рд╛рдВ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ), рдФрд░ рдСрдкрд░реЗрд╢рди рдХреЗ _ss рд╕рдВрд╕реНрдХрд░рдг ( рдПрдХреНрд╕рдПрдордПрдо рд░рдЬрд┐рд╕реНрдЯрд░ рдХреЗ рдирд┐рдЪрд▓реЗ рдлреНрд▓реЛрдЯ рдореЗрдВ рдШрдЯрдХ рдХреЗ рд╕рд╛рде рд╡рд┐рд╢реЗрд╖ рд░реВрдк рд╕реЗ рд╕рдВрдЪрд╛рд▓рди) рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдРрд╕рд╛ рдХрд░рдирд╛ рдмрд╣реБрдд рдЖрд╕рд╛рди рд╣реИ, рдПрдХреНрд╕, рд╡рд╛рдИ рдХреЛ рдЫреВрдиреЗ рдХреЗ рдмрд┐рдирд╛ ред z рдЗрд╕рд▓рд┐рдП, SSE рд░рдЬрд┐рд╕реНрдЯрд░ рдореЗрдВ, рд╡реЗрдХреНрдЯрд░ рдХреЛ рдПрдХ рд╕рдордЭрдиреЗ рдпреЛрдЧреНрдп рдХреНрд░рдо x, y, z, w , рдФрд░ рдореЗрдореЛрд░реА рдореЗрдВ рд░рд┐рд╡рд░реНрд╕ w, z, y, x рдореЗрдВ рд╕рдВрдЧреНрд░рд╣рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред

рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рд╕рднреА рдЧреБрдгрд╛ рд╡рд┐рдХрд▓реНрдк рд╡реНрдпрдХреНрддрд┐рдЧрдд рдХрд╛рд░реНрдпреЛрдВ рджреНрд╡рд╛рд░рд╛ рднреА рд▓рд╛рдЧреВ рдХрд┐рдП рдЬрд╛рддреЗ рд╣реИрдВред рдпрд╣ рдЗрд╕рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рдореИрдВ рдЙрдиреНрд╣реЗрдВ рдЗрдЪреНрдЫрд┐рдд рд╡рд┐рдХрд▓реНрдк рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдЗрдЪреНрдЫрд┐рдд рд╡рд┐рдХрд▓реНрдк рдХреЗ рд╡рд┐рдХрд▓реНрдк рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реВрдВред рдпрд╣рд╛рдБ рд╡рд░реНрдгрд┐рдд рд╣реИред

рд╣рдо рдмреБрдирд┐рдпрд╛рджреА рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рддреЗ рд╣реИрдВ


рдЫреЛрд░реЛрдВ рдХреЗ рд╕рд╛рде рдЧреБрдгрд╛, рдкрдВрдХреНрддрд┐ рдХрд╛ рдЖрджреЗрд╢ рджрд┐рдпрд╛


рддрддреНрд╡реЛрдВ рдХреЗ рд▓рд╛рдЗрди рд▓реЗрдЖрдЙрдЯ рдХреЗ рд▓рд┐рдП рд╡рд┐рдХрд▓реНрдк
for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { r[i][j] = 0.f; for (int k = 0; k < 4; ++k) { r[i][j] += m[i][k] * n[k][j]; } } } 


рдпрд╣рд╛рдВ рд╕рдм рдХреБрдЫ рд╕рд░рд▓ рдФрд░ рд╕реНрдкрд╖реНрдЯ рд╣реИред рдкреНрд░рддреНрдпреЗрдХ рддрддреНрд╡ рдХреЗ рд▓рд┐рдП рд╣рдо 4 рдЧреБрдгрд╛ рдФрд░ 3 рдЬреЛрдбрд╝ рдХрд░рддреЗ рд╣реИрдВред рдХреБрд▓ рдореЗрдВ, рдпреЗ 64 рдЧреБрдгрд╛ рдФрд░ 48 рдЬреЛрдбрд╝ рд╣реИрдВред рдФрд░ рдпрд╣ рд░рд┐рдХреЙрд░реНрдб рддрддреНрд╡реЛрдВ рдХреЗ рдкрдврд╝рдиреЗ рдХреЛ рдзреНрдпрд╛рди рдореЗрдВ рд░рдЦреЗ рдмрд┐рдирд╛ рд╣реИред

рд╕рдм рдХреБрдЫ рджреБрдЦ рдХреА рдмрд╛рдд рд╣реИ, рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВред рдЗрд╕ рд╡рд┐рдХрд▓реНрдк рдХреЗ рд▓рд┐рдП, рдЖрдВрддрд░рд┐рдХ рдЪрдХреНрд░ рдХреЗ рд▓рд┐рдП, IACA рдиреЗ рдЬрд╛рд░реА рдХрд┐рдпрд╛: x86 рд╡рд┐рдзрд╛рдирд╕рднрд╛ рдХреЗ рд▓рд┐рдП 3.65 рдШрдбрд╝реА рдЪрдХреНрд░ рдФрд░ x64 рд╡рд┐рдзрд╛рдирд╕рднрд╛ рдХреЗ рд▓рд┐рдП 2.97 рдШрдбрд╝реА ред рдпрд╣ рдордд рдкреВрдЫреЛ рдХрд┐ рднрд┐рдиреНрдирд╛рддреНрдордХ рд╕рдВрдЦреНрдпрд╛рдПрдБ рдХреНрдпреЛрдВ рд╣реИрдВред рдореБрдЭреЗ рдирд╣реАрдВ рдкрддрд╛ IACA 2.1 рдХреЛ рдЗрд╕рд╕реЗ рдиреБрдХрд╕рд╛рди рдирд╣реАрдВ рд╣реБрдЖред рдХрд┐рд╕реА рднреА рд╕реНрдерд┐рддрд┐ рдореЗрдВ, рдЗрди рд╕рдВрдЦреНрдпрд╛рдУрдВ рдХреЛ рд▓рдЧрднрдЧ 4 * 4 * 4 = 64 рд╕реЗ рдЧреБрдгрд╛ рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред рднрд▓реЗ рд╣реА рдЖрдк x64 рд▓реЗрддреЗ рд╣реИрдВ, рдкрд░рд┐рдгрд╛рдо 192 рдЙрдкрд╛рдпреЛрдВ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╣реИред рдпрд╣ рд╕реНрдкрд╖реНрдЯ рд╣реИ рдХрд┐ рдпрд╣ рдПрдХ рдореЛрдЯрд╛ рдЕрдиреБрдорд╛рди рд╣реИред рдореБрдЭреЗ рдЗрд╕ рд╡рд┐рдХрд▓реНрдк рдХреЗ рд▓рд┐рдП рдкреНрд░рджрд░реНрд╢рди рдХрд╛ рдЕрдзрд┐рдХ рд╕рдЯреАрдХ рдореВрд▓реНрдпрд╛рдВрдХрди рдХрд░рдиреЗ рдХреА рдмрд╛рдд рдирд╣реАрдВ рджрд┐рдЦрддреА рд╣реИред

рд▓реВрдкрд┐рдВрдЧ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди, рдХреЙрд▓рдо рдХрд╛ рдЖрджреЗрд╢ рджрд┐рдпрд╛


рдЯреНрд░рд╛рдВрд╕рдкреЛрдЬрд╝реНрдб рдореИрдЯреНрд░рд┐рдХреНрд╕, рд░рд┐рдпрд░ рдкрдВрдХреНрддрд┐ рдФрд░ рдХреЙрд▓рдо рдЗрдВрдбреЗрдХреНрд╕
 for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { r[j][i] = 0.f; for (int k = 0; k < 4; ++k) { r[j][i] += m[k][i] * n[j][k]; } } } 


рд╕рд╛рдЗрдХрд┐рд▓ рдЧреБрдгрд╛, SIMD рдЙрдиреНрдореБрдЦ рднрдВрдбрд╛рд░рдг


рдореЗрдореЛрд░реА рдореЗрдВ рд░рд┐рд╡рд░реНрд╕ рдСрд░реНрдбрд░ рдореЗрдВ рд▓рд╛рдЗрдиреЛрдВ рдХрд╛ рднрдВрдбрд╛рд░рдг рдЬреЛрдбрд╝рд╛ рдЬрд╛рддрд╛ рд╣реИ
 for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { r[j][3-i] = 0.f; for (int k = 0; k < 4; ++k) { r[j][3-i] += m[k][3-i] * n[j][3-k]; } } } 


рдпрд╣ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреБрдЫ рд╣рдж рддрдХ рдЗрд╕ рд╕рдордЭ рдХреЛ рд╕рд░рд▓ рдХрд░рддрд╛ рд╣реИ рдХрд┐ рдЕрдВрджрд░ рдХреНрдпрд╛ рд╣реЛ рд░рд╣рд╛ рд╣реИ, рд▓реЗрдХрд┐рди рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдкрд░реНрдпрд╛рдкреНрдд рдирд╣реАрдВ рд╣реИред

рд╕рд╣рд╛рдпрдХ рд╡рд░реНрдЧ


рд╕рдВрджрд░реНрдн рдФрд░ рдбрд┐рдмрдЧрд┐рдВрдЧ рдХреЛрдб рдХреЛ рд╕рдордЭрдиреЗ рдФрд░ рд▓рд┐рдЦрдиреЗ рдХреА рд╕реБрд╡рд┐рдзрд╛ рдХреЗ рд▓рд┐рдП, рд╕рд╣рд╛рдпрдХ рд╡рд░реНрдЧреЛрдВ рдХреЗ рдПрдХ рдЬреЛрдбрд╝реЗ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдирд╛ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд╣реИред рдЬреНрдпрд╛рджрд╛ рдХреБрдЫ рдирд╣реАрдВ, рд╕рдм рдХреБрдЫ рдХреЗрд╡рд▓ рд╕рдордЭрдиреЗ рдХреЗ рд▓рд┐рдП рд╣реИред рдореИрдВ рдзреНрдпрд╛рди рджреЗрддрд╛ рд╣реВрдВ рдХрд┐ рдкреВрд░реНрдг-рд╕рджрд┐рд╢ рдФрд░ рдореИрдЯреНрд░рд┐рдХреНрд╕ рдХрдХреНрд╖рд╛рдУрдВ рдХрд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдПрдХ рдЕрд▓рдЧ рдХрдард┐рди рдкреНрд░рд╢реНрди рд╣реИ, рдФрд░ рдЗрд╕ рд▓реЗрдЦ рдХреЗ рд╡рд┐рд╖рдп рдореЗрдВ рд╢рд╛рдорд┐рд▓ рдирд╣реАрдВ рд╣реИред

рд╡реЗрдХреНрдЯрд░ рдФрд░ рдореИрдЯреНрд░рд┐рдХреНрд╕ рдХрдХреНрд╖рд╛рдПрдВ
 struct alignas(sizeof(__m128)) vec4 { union { struct { float w, z, y, x; }; __m128 fmm; float arr[4]; }; vec4() {} vec4(float a, float b, float c, float d) : w(d), z(c), y(b), x(a) {} static bool equ(float const a, float const b, float t = .00001f) { return fabs(ab) < t; } bool operator == (vec4 const& v) const { return equ(x, vx) && equ(y, vy) && equ(z, vz) && equ(w, vw); } }; struct alignas(sizeof(__m256)) mtx4 { //           union { struct { float _30, _20, _10, _00, _31, _21, _11, _01, _32, _22, _12, _02, _33, _23, _13, _03; }; __m128 r[4]; __m256 s[2]; vec4 v[4]; }; //    mtx4() {} mtx4( float i00, float i01, float i02, float i03, float i10, float i11, float i12, float i13, float i20, float i21, float i22, float i23, float i30, float i31, float i32, float i33) : _00(i00), _01(i01), _02(i02), _03(i03) , _10(i10), _11(i11), _12(i12), _13(i13) , _20(i20), _21(i21), _22(i22), _23(i23) , _30(i30), _31(i31), _32(i32), _33(i33) {} //      operator __m128 const* () const { return r; } operator __m128* () { return r; } //   bool operator == (mtx4 const& m) const { return v[0]==mv[0] && v[1]==mv[1] && v[2]==mv[2] && v[3]==mv[3]; } //  static mtx4 identity() { return mtx4( 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f); } static mtx4 zero() { return mtx4( 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f); } }; 


рдкрд░реАрдХреНрд╖рдг рдХреЗ рд▓рд┐рдП рд╕рдВрджрд░реНрдн рд╕рдорд╛рд░реЛрд╣


рдЪреВрдВрдХрд┐ рдореИрдЯреНрд░рд┐рдХреНрд╕ рдореЗрдВ рддрддреНрд╡реЛрдВ рдХрд╛ рд╕реНрд╡реАрдХреГрдд рдХреНрд░рдо рд╕рдордЭ рдХреЛ рдмрд╣реБрдд рдЬрдЯрд┐рд▓ рдХрд░рддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рд╣рдо рд╕рдВрджрд░реНрдн рд╕реНрдкрд╖реНрдЯ рдлрд╝рдВрдХреНрд╢рди рд╕реЗ рднреА рдкрд░реЗрд╢рд╛рди рдирд╣реАрдВ рд╣реЛрдВрдЧреЗ, рдЬреЛ рднрд╡рд┐рд╖реНрдп рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдореЗрдВ рджрд┐рдЦрд╛рдПрдЧрд╛ рдХрд┐ рд╕рдм рдХреБрдЫ рд╕рд╣реА рдврдВрдЧ рд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред рд╣рдо рдЗрд╕рдХреЗ рд╕рд╛рде рдмрд╛рдж рдХреЗ рдкрд░рд┐рдгрд╛рдореЛрдВ рдХреА рддреБрд▓рдирд╛ рдХрд░реЗрдВрдЧреЗред

рдЗрд╕реЗ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП, рдмрд╕ рдЪрдХреНрд░ рдХреЛ рд▓реЗрдВ рдФрд░ рдЙрд╕рдХрд╛ рд╡рд┐рд╕реНрддрд╛рд░ рдХрд░реЗрдВ
 void mul_mtx4_mtx4_unroll(__m128* const _r, __m128 const* const _m, __m128 const* const _n) { mtx4 const& m = **reinterpret_cast<mtx4 const* const*>(&_m); mtx4 const& n = **reinterpret_cast<mtx4 const* const*>(&_n); mtx4& r = **reinterpret_cast<mtx4* const*>(&_r); r._00 = m._00*n._00 + m._01*n._10 + m._02*n._20 + m._03*n._30; r._01 = m._00*n._01 + m._01*n._11 + m._02*n._21 + m._03*n._31; r._02 = m._00*n._02 + m._01*n._12 + m._02*n._22 + m._03*n._32; r._03 = m._00*n._03 + m._01*n._13 + m._02*n._23 + m._03*n._33; r._10 = m._10*n._00 + m._11*n._10 + m._12*n._20 + m._13*n._30; r._11 = m._10*n._01 + m._11*n._11 + m._12*n._21 + m._13*n._31; r._12 = m._10*n._02 + m._11*n._12 + m._12*n._22 + m._13*n._32; r._13 = m._10*n._03 + m._11*n._13 + m._12*n._23 + m._13*n._33; r._20 = m._20*n._00 + m._21*n._10 + m._22*n._20 + m._23*n._30; r._21 = m._20*n._01 + m._21*n._11 + m._22*n._21 + m._23*n._31; r._22 = m._20*n._02 + m._21*n._12 + m._22*n._22 + m._23*n._32; r._23 = m._20*n._03 + m._21*n._13 + m._22*n._23 + m._23*n._33; r._30 = m._30*n._00 + m._31*n._10 + m._32*n._20 + m._33*n._30; r._31 = m._30*n._01 + m._31*n._11 + m._32*n._21 + m._33*n._31; r._32 = m._30*n._02 + m._31*n._12 + m._32*n._22 + m._33*n._32; r._33 = m._30*n._03 + m._31*n._13 + m._32*n._23 + m._33*n._33; } 


рд╢рд╛рд╕реНрддреНрд░реАрдп рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдпрд╣рд╛рдВ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдЪрд┐рддреНрд░рд┐рдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рдПрдХ рдЧрд▓рддреА рдХрд░рдирд╛ рдореБрд╢реНрдХрд┐рд▓ рд╣реИ (рд▓реЗрдХрд┐рди рдЖрдк :-)) рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдЙрд╕ рдкрд░, IACA рдиреЗ рдЬрд╛рд░реА рдХрд┐рдпрд╛: x86 - 69.95 рдЙрдкрд╛рдп, x64 - 64 рдЙрдкрд╛рдп ред рдпрд╣рд╛рдВ рд▓рдЧрднрдЧ 64 рдЪрдХреНрд░ рд╣реИрдВ рдФрд░ рд╣рдо рднрд╡рд┐рд╖реНрдп рдореЗрдВ рдЗрд╕ рдСрдкрд░реЗрд╢рди рдХреЗ рддреНрд╡рд░рдг рдХреЛ рджреЗрдЦреЗрдВрдЧреЗред

SSE рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди


рдХреНрд▓рд╛рд╕рд┐рдХ SSE рдПрд▓реНрдЧреЛрд░рд┐рдердо


рдХреНтАНрдпреЛрдВ рдХреНрд▓рд╛рд╕рд┐рдХ? рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рд▓рдВрдмреЗ рд╕рдордп рд╕реЗ рдПрдордПрд╕рд╡реАрдПрд╕ рдХреЗ рд╣рд┐рд╕реНрд╕реЗ рдХреЗ рд░реВрдк рдореЗрдВ рдПрдлрд╡реАрдИрд╕реА рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдореЗрдВ рд╣реИред рд╢реБрд░реВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдо рд▓рд┐рдЦреЗрдВрдЧреЗ рдХрд┐ рд╣рдо рдПрд╕рдПрд╕рдИ рд░рдЬрд┐рд╕реНрдЯрд░реЛрдВ рдореЗрдВ рдореИрдЯреНрд░рд┐рдХреНрд╕ рддрддреНрд╡ рдХреИрд╕реЗ рдкреНрд░рд╕реНрддреБрдд рдХрд░рддреЗ рд╣реИрдВред рдпрд╣ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдпрд╣рд╛рдБ рд╕рд░рд▓ рджрд┐рдЦрддрд╛ рд╣реИред рдмрд╕ рдПрдХ рдЯреНрд░рд╛рдВрд╕рдкреЛрдВрдб рдореИрдЯреНрд░рд┐рдХреНрд╕ред

 //     00, 10, 20, 30 // m[0] -  SIMD /   01, 11, 21, 31 // m[1] 02, 12, 22, 32 // m[2] 03, 13, 23, 33 // m[3] 

рд╣рдо рдКрдкрд░ рджрд┐рдП рдЧрдП рд╡реЗрд░рд┐рдПрдВрдЯ рдХрд╛ рдЕрдирд┐рдпрдВрддреНрд░рд┐рдд рдХреЛрдб рд▓реЗрддреЗ рд╣реИрдВред рдХрд┐рд╕реА рддрд░рд╣ рд╡рд╣ SSE рдХреЗ рд▓рд┐рдП рдЕрдорд┐рддреНрд░ рд╣реИред рдкрдВрдХреНрддрд┐рдпреЛрдВ рдХреЗ рдкрд╣рд▓реЗ рд╕рдореВрд╣ рдореЗрдВ рдкрд░рд┐рдгрд╛рдореА рдореИрдЯреНрд░рд┐рдХреНрд╕ рдХреЗ рдХреЙрд▓рдо рдХреЗ рдкрд░рд┐рдгрд╛рдо рд╣реЛрддреЗ рд╣реИрдВ: r._00, r._01, r._02, r._03 ред рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдпрд╣ рдХреЙрд▓рдо рд╣реИ, рд▓реЗрдХрд┐рди рд╣рдореЗрдВ рдПрдХ рдкрдВрдХреНрддрд┐ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рд╣рд╛рдВ, рдФрд░ рдореА , рдПрди рдЧрдгрдирд╛ рдХреЗ рд▓рд┐рдП рдЕрд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рджрд┐рдЦрддреЗ рд╣реИрдВред рдЗрд╕рд▓рд┐рдП, рд╣рдо рдПрд▓реНрдЧреЛрд░рд┐рдердо рдХреА рдкрдВрдХреНрддрд┐рдпреЛрдВ рдХреЛ рдкреБрдирд░реНрд╡реНрдпрд╡рд╕реНрдерд┐рдд рдХрд░рддреЗ рд╣реИрдВ рддрд╛рдХрд┐ рдкрд░рд┐рдгрд╛рдо рдЖрд░ рдкрдВрдХреНрддрд┐-рд╡рд╛рд░ рд╣реЛред

 //  ,     r[0] r00 = m00*n00 + m01*n10 + m02*n20 + m03*n30; r10 = m10*n00 + m11*n10 + m12*n20 + m13*n30; r20 = m20*n00 + m21*n10 + m22*n20 + m23*n30; r30 = m30*n00 + m31*n10 + m32*n20 + m33*n30; //  ,     r[1] r01 = m00*n01 + m01*n11 + m02*n21 + m03*n31; r11 = m10*n01 + m11*n11 + m12*n21 + m13*n31; r21 = m20*n01 + m21*n11 + m22*n21 + m23*n31; r31 = m30*n01 + m31*n11 + m32*n21 + m33*n31; //  ,     r[2] r02 = m00*n02 + m01*n12 + m02*n22 + m03*n32; r12 = m10*n02 + m11*n12 + m12*n22 + m13*n32; r22 = m20*n02 + m21*n12 + m22*n22 + m23*n32; r32 = m30*n02 + m31*n12 + m32*n22 + m33*n32; //  ,     r[3] r03 = m00*n03 + m01*n13 + m02*n23 + m03*n33; r13 = m10*n03 + m11*n13 + m12*n23 + m13*n33; r23 = m20*n03 + m21*n13 + m22*n23 + m23*n33; r33 = m30*n03 + m31*n13 + m32*n23 + m33*n33; 

рд▓реЗрдХрд┐рди рдпрд╣ рдкрд╣рд▓реЗ рд╕реЗ рдХрд╛рдлреА рдмреЗрд╣рддрд░ рд╣реИред рдХреНрдпрд╛, рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рдХреНрдпрд╛ рд╣рдо рджреЗрдЦрддреЗ рд╣реИрдВ? рдкреНрд░рддреНрдпреЗрдХ рд╕рдореВрд╣ рдореЗрдВ рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХреЗ рдХреЙрд▓рдо рдХреЗ рдЕрдиреБрд╕рд╛рд░, рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рд╢рд╛рдорд┐рд▓ рдореИрдЯреНрд░рд┐рдХреНрд╕ рдХреА рдкрдВрдХреНрддрд┐рдпрд╛рдБ рд╣реИрдВ:
 m [0] = {00,10,20,30}, m [1] = {01,11,21,31}, m [2] = {02,12,22,32}, m [3] = {} 03,13,23,33,
рдЬреЛ рдореИрдЯреНрд░рд┐рдХреНрд╕ n рдХреЗ рдПрдХ рд╣реА рддрддреНрд╡ рджреНрд╡рд╛рд░рд╛ рдЧреБрдгрд╛ рдХрд┐рдП рдЬрд╛рддреЗ рд╣реИрдВред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдкрд╣рд▓реЗ рд╕рдореВрд╣ рдХреЗ рд▓рд┐рдП рдпрд╣ рд╣реИ: n._00, n._10, n._20, n._30 ред рдФрд░ рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХреА рдкрдВрдХреНрддрд┐рдпреЛрдВ рдХреЗ рдкреНрд░рддреНрдпреЗрдХ рд╕рдореВрд╣ рдХреЗ рд▓рд┐рдП рдореИрдЯреНрд░рд┐рдХреНрд╕ рдХреЗ рддрддреНрд╡ рдлрд┐рд░ рд╕реЗ рдореИрдЯреНрд░рд┐рдХреНрд╕ рдХреА рдПрдХ рдкрдВрдХреНрддрд┐ рдореЗрдВ рдЭреВрда рдмреЛрд▓рддреЗ рд╣реИрдВред

рддрдм рд╕рдм рдХреБрдЫ рд╕рд░рд▓ рд╣реИ: рд╣рдо рдХреЗрд╡рд▓ рдореИрдЯреНрд░рд┐рдХреНрд╕ рдореАрдЯрд░ рдХреА рдкрдВрдХреНрддрд┐рдпреЛрдВ рдХреЛ рд╕реВрдЪрдХрд╛рдВрдХ рджреНрд╡рд╛рд░рд╛ рд▓реЗрддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рддрддреНрд╡реЛрдВ n рдХреЗ рд▓рд┐рдП , рд╣рдо рдЗрд╕рдХреА рдкрдВрдХреНрддрд┐ рд▓реЗрддреЗ рд╣реИрдВ рдФрд░ рдЗрд╕реЗ рд░рдЬрд┐рд╕реНрдЯрд░ рдХреЗ рд╕рднреА 4 рддрддреНрд╡реЛрдВ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдлреЗрд░рдмрджрд▓ рдХрд░рддреЗ рд╣реИрдВ, рд░рдЬрд┐рд╕реНрдЯрд░ рдореЗрдВ рдореИрдЯреНрд░рд┐рдХреНрд╕ рдореАрдЯрд░ рдХреА рдкрдВрдХреНрддрд┐ рджреНрд╡рд╛рд░рд╛ рдЧреБрдгрд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдПред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рддрддреНрд╡ n._00 рдХреЗ рд▓рд┐рдП (рдпрд╛рдж рд░рдЦреЗрдВ рдХрд┐ рд░рдЬрд┐рд╕реНрдЯрд░ рдореЗрдВ рдЗрд╕рдХреА рдкрд╛рд░реА рдореЗрдВ рд╕реВрдЪрдХрд╛рдВрдХ 3 рд╣реИ), рдпрд╣ рд╣реЛрдЧрд╛:
  _mm_shuffle_ps (n [0], n [0], _MM_SHUFFLE (3,3,3,3)) 

рд╕рд░рд▓реАрдХреГрдд рд░реВрдк рдореЗрдВ, рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддрд╛ рд╣реИ:

 //   n[0]={00,10,20,30} r[0] = m[0] * n00 + m[1] * n10 + m[2] * n20 + m[3] * n30; //   n[1]={01,11,21,31} r[1] = m[0] * n01 + m[1] * n11 + m[2] * n21 + m[3] * n31; //   n[2]={02,12,22,32} r[2] = m[0] * n02 + m[1] * n12 + m[2] * n22 + m[3] * n32; //   n[3]={03,13,23,33} r[3] = m[0] * n03 + m[1] * n13 + m[2] * n23 + m[3] * n33; 

рдмреБрдирд┐рдпрд╛рджреА рдПрд╕рдПрд╕рдИ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди
 void mul_mtx4_mtx4_sse_v1(__m128* const r, __m128 const* const m, __m128 const* const n) { r[0] = _mm_add_ps( _mm_add_ps( _mm_mul_ps(m[0], _mm_shuffle_ps(n[0], n[0], _MM_SHUFFLE(3,3,3,3))), _mm_mul_ps(m[1], _mm_shuffle_ps(n[0], n[0], _MM_SHUFFLE(2,2,2,2)))), _mm_add_ps( _mm_mul_ps(m[2], _mm_shuffle_ps(n[0], n[0], _MM_SHUFFLE(1,1,1,1))), _mm_mul_ps(m[3], _mm_shuffle_ps(n[0], n[0], _MM_SHUFFLE(0,0,0,0))))); r[1] = _mm_add_ps( _mm_add_ps( _mm_mul_ps(m[0], _mm_shuffle_ps(n[1], n[1], _MM_SHUFFLE(3,3,3,3))), _mm_mul_ps(m[1], _mm_shuffle_ps(n[1], n[1], _MM_SHUFFLE(2,2,2,2)))), _mm_add_ps( _mm_mul_ps(m[2], _mm_shuffle_ps(n[1], n[1], _MM_SHUFFLE(1,1,1,1))), _mm_mul_ps(m[3], _mm_shuffle_ps(n[1], n[1], _MM_SHUFFLE(0,0,0,0))))); r[2] = _mm_add_ps( _mm_add_ps( _mm_mul_ps(m[0], _mm_shuffle_ps(n[2], n[2], _MM_SHUFFLE(3,3,3,3))), _mm_mul_ps(m[1], _mm_shuffle_ps(n[2], n[2], _MM_SHUFFLE(2,2,2,2)))), _mm_add_ps( _mm_mul_ps(m[2], _mm_shuffle_ps(n[2], n[2], _MM_SHUFFLE(1,1,1,1))), _mm_mul_ps(m[3], _mm_shuffle_ps(n[2], n[2], _MM_SHUFFLE(0,0,0,0))))); r[3] = _mm_add_ps( _mm_add_ps( _mm_mul_ps(m[0], _mm_shuffle_ps(n[3], n[3], _MM_SHUFFLE(3,3,3,3))), _mm_mul_ps(m[1], _mm_shuffle_ps(n[3], n[3], _MM_SHUFFLE(2,2,2,2)))), _mm_add_ps( _mm_mul_ps(m[2], _mm_shuffle_ps(n[3], n[3], _MM_SHUFFLE(1,1,1,1))), _mm_mul_ps(m[3], _mm_shuffle_ps(n[3], n[3], _MM_SHUFFLE(0,0,0,0))))); } 


рдЕрдм рд╣рдо рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХреЗ рддрддреНрд╡реЛрдВ рдХреЛ рд╕рдВрдмрдВрдзрд┐рдд рдлреЗрд░рдмрджрд▓ рдореЗрдВ рдмрджрд▓рддреЗ рд╣реИрдВ , _mm_mul_ps рджреНрд╡рд╛рд░рд╛ рдЧреБрдгрд╛, _mm_add_ps рджреНрд╡рд╛рд░рд╛ рдпреЛрдЧ, рдФрд░ рдЖрдк рдХрд░ рд░рд╣реЗ рд╣реИрдВ ред рдпрд╣ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред рдХреЛрдб, рд╣рд╛рд▓рд╛рдВрдХрд┐, рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХреА рддреБрд▓рдирд╛ рдореЗрдВ рдмрд╣реБрдд рдЦрд░рд╛рдм рд▓рдЧ рд░рд╣рд╛ рдерд╛ред рдЗрд╕ рдХреЛрдб рдХреЗ рд▓рд┐рдП, IACA рдиреЗ рдЬрд╛рд░реА рдХрд┐рдпрд╛: x86 - 18.89, x64 - 16 рдЪрдХреНрд░ ред рдпрд╣ рдкрд┐рдЫрд▓реЗ рд╡рд╛рд▓реЗ рдХреА рддреБрд▓рдирд╛ рдореЗрдВ 4 рдЧреБрдирд╛ рддреЗрдЬ рд╣реИред SSE рд░рдЬрд┐рд╕реНрдЯрд░ рдореЗрдВ 4th рдлреНрд▓реЛрдЯред рд▓рдЧрднрдЧ рд░реИрдЦрд┐рдХ рд╕рдВрдмрдВрдзред

рдПрд╕рдПрд╕рдИ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЛ рд╕рдЬрд╛рдиреЗ


рдлрд┐рд░ рднреА, рдХреЛрдб рдореЗрдВ рдпрд╣ рднрдпрд╛рдирдХ рд▓рдЧ рд░рд╣рд╛ рд╣реИред рд╣рдо рдереЛрдбрд╝рд╛ рд╕рд╛ рд╕рдВрд╢реНрд▓рд┐рд╖реНрдЯ рдЪреАрдиреА рд▓рд┐рдЦрдХрд░ рдЗрд╕реЗ рд╕реБрдзрд╛рд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░реЗрдВрдЧреЗред

рд╕рдВрдЪрд╛рд▓рдХ рдФрд░ рд╕реБрдзрд╛рд░рдХ
 //        ( -    namespace) __m128 operator + (__m128 const a, __m128 const b) { return _mm_add_ps(a, b); } __m128 operator - (__m128 const a, __m128 const b) { return _mm_sub_ps(a, b); } __m128 operator * (__m128 const a, __m128 const b) { return _mm_mul_ps(a, b); } __m128 operator / (__m128 const a, __m128 const b) { return _mm_div_ps(a, b); } //_mm_shuffle_ps(u, v, _MM_SHUFFLE(3,2,1,0))   shuf<3,2,1,0>(u, v) template <int a, int b, int c, int d> __m128 shuf(__m128 const u, __m128 const v) { return _mm_shuffle_ps(u, v, _MM_SHUFFLE(a, b, c, d)); } template <int a, int b, int c, int d> __m128 shuf(__m128 const v) { return _mm_shuffle_ps(v, v, _MM_SHUFFLE(a, b, c, d)); } //    template <int i> __m128 shuf(__m128 const u, __m128 const v) { return _mm_shuffle_ps(u, v, _MM_SHUFFLE(i, i, i, i)); } template <int i> __m128 shuf(__m128 const v) { return _mm_shuffle_ps(v, v, _MM_SHUFFLE(i, i, i, i)); } //  float       , //    ,    template <int a, int b, int c, int d> __m128 shufd(__m128 const v) { return _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(v), _MM_SHUFFLE(a, b, c, d))); } template <int i> __m128 shufd(__m128 const v) { return _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(v), _MM_SHUFFLE(i, i, i, i))); } 


рдХрдВрдкрд╛рдЗрд▓рд░ рдЗрди рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдЗрдирд▓рд╛рдЗрди рдХрд░ рд╕рдХрддрд╛ рд╣реИ (рд╣рд╛рд▓рд╛рдВрдХрд┐ рдХрднреА-рдХрднреА __рдлреЛрд░реНрд╕рд┐рдирд▓рд╛рдЗрди рдХреЗ рдмрд┐рдирд╛ рдХрд┐рд╕реА рднреА рддрд░рд╣ рд╕реЗ)ред

рдЗрд╕рд▓рд┐рдП рдХреЛрдб рдмрджрд▓ рд░рд╣рд╛ рд╣реИ ...
 void mul_mtx4_mtx4_sse_v2(__m128* const r, __m128 const* const m, __m128 const* const n) { r[0] = m[0]*shuf<3>(n[0]) + m[1]*shuf<2>(n[0]) + m[2]*shuf<1>(n[0]) + m[3]*shuf<0>(n[0]); r[1] = m[0]*shuf<3>(n[1]) + m[1]*shuf<2>(n[1]) + m[2]*shuf<1>(n[1]) + m[3]*shuf<0>(n[1]); r[2] = m[0]*shuf<3>(n[2]) + m[1]*shuf<2>(n[2]) + m[2]*shuf<1>(n[2]) + m[3]*shuf<0>(n[2]); r[3] = m[0]*shuf<3>(n[3]) + m[1]*shuf<2>(n[3]) + m[2]*shuf<1>(n[3]) + m[3]*shuf<0>(n[3]); } 


рдФрд░ рдЗрд╕рд▓рд┐рдП рдпрд╣ рдкрд╣рд▓реЗ рд╕реЗ рдмрд╣реБрдд рдмреЗрд╣рддрд░ рдФрд░ рдЕрдзрд┐рдХ рдкрдардиреАрдп рд╣реИред рдЗрд╕рдХреЗ рд▓рд┐рдП, IACA рдиреЗ рд▓рдЧрднрдЧ рдЕрдкреЗрдХреНрд╖рд┐рдд рдкрд░рд┐рдгрд╛рдо рдХрд╛ рдЙрддреНрдкрд╛рджрди рдХрд┐рдпрд╛: x86 - 19 (рдФрд░ рднрд┐рдиреНрдирд╛рддреНрдордХ рдХреНрдпреЛрдВ рдирд╣реАрдВ?), X64 - 16 ред рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рдкреНрд░рджрд░реНрд╢рди рдирд╣реАрдВ рдмрджрд▓рд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдХреЛрдб рдмрд╣реБрдд рдЕрдзрд┐рдХ рд╕реБрдВрджрд░ рдФрд░ рд╕рдордЭрдиреЗ рдпреЛрдЧреНрдп рд╣реИред

рднрд╡рд┐рд╖реНрдп рдХреЗ рдЕрдиреБрдХреВрд▓рди рдореЗрдВ рдереЛрдбрд╝рд╛ рдпреЛрдЧрджрд╛рди


рдЖрдЗрдП рдПрдХ рдлрд╝рдВрдХреНрд╢рди рдХреЗ рд╕реНрддрд░ рдкрд░ рдПрдХ рдФрд░ рд╕реБрдзрд╛рд░ рдкреЗрд╢ рдХрд░реЗрдВ рдЬреЛ рд╣рд╛рд▓ рд╣реА рдореЗрдВ рд▓реЛрд╣реЗ рдХреЗ рд╕рдВрд╕реНрдХрд░рдг рдореЗрдВ рджрд┐рдЦрд╛рдИ рджрд┐рдпрд╛ред рдСрдкрд░реЗрд╢рди рдорд▓реНрдЯреАрдкрд▓-рдРрдб (fma) ред fma (рдП, рдмреА, рд╕реА) = рдП * рдмреА + рд╕реА ред

рдХрдИ-рдХрдИ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди
 __m128 mad(__m128 const a, __m128 const b, __m128 const c) { return _mm_add_ps(_mm_mul_ps(a, b), c); } 


рдпрд╣ рдХреНрдпреЛрдВ рдЖрд╡рд╢реНрдпрдХ рд╣реИ? рднрд╡рд┐рд╖реНрдп рдХреЗ рдЕрдиреБрдХреВрд▓рди рдХреЗ рд▓рд┐рдП рд╕рдмрд╕реЗ рдкрд╣рд▓реЗред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдЖрдк рдмрд╕ рдЙрд╕реА рдореИрдХреНрд░реЛрдЬрд╝ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рддреИрдпрд╛рд░ рдХреЛрдб рдореЗрдВ fma рдХреЗ рд╕рд╛рде рдкрд╛рдЧрд▓ рдХреЛ рдмрджрд▓ рд╕рдХрддреЗ рд╣реИрдВ рдЬреИрд╕реЗ рдЖрдк рдЪрд╛рд╣рддреЗ рд╣реИрдВред рд▓реЗрдХрд┐рди рдЕрдм рд╣рдо рдЕрдиреБрдХреВрд▓рди рдХреА рдиреАрдВрд╡ рд░рдЦреЗрдВрдЧреЗ:

рдмрд╣реБ-рдЬреЛрдбрд╝ рдХреЗ рд╕рд╛рде рднрд┐рдиреНрди
 void mul_mtx4_mtx4_sse_v3(__m128* const r, __m128 const* const m, __m128 const* const n) { r[0] = mad(m[0], shuf<3>(n[0]), m[1]*shuf<2>(n[0])) + mad(m[2], shuf<1>(n[0]), m[3]*shuf<0>(n[0])); r[1] = mad(m[0], shuf<3>(n[1]), m[1]*shuf<2>(n[1]) + mad(m[2], shuf<1>(n[1]), m[3]*shuf<0>(n[1])); r[2] = mad(m[0], shuf<3>(n[2]), m[1]*shuf<2>(n[2])) + mad(m[2], shuf<1>(n[2]), m[3]*shuf<0>(n[2])); r[3] = mad(m[0], shuf<3>(n[3]), m[1]*shuf<2>(n[3])) + mad(m[2], shuf<1>(n[3]), m[3]*shuf<0>(n[3])); } 


IACA: x86 - 18.89, x64 - 16 ред рдлрд┐рд░ рд╕реЗ рднрд┐рдиреНрдирд╛рддреНрдордХред рдлрд┐рд░ рднреА, IACA рдХрднреА-рдХрднреА рдЕрдЬреАрдм рдкрд░рд┐рдгрд╛рдо рдкреИрджрд╛ рдХрд░рддрд╛ рд╣реИред рдХреЛрдб рдЗрддрдирд╛ рдирд╣реАрдВ рдмрджрд▓рд╛ рд╣реИред рд╢рд╛рдпрдж рдереЛрдбрд╝рд╛ рдмреБрд░рд╛ рднреАред рд▓реЗрдХрд┐рди рдЕрдиреБрдХреВрд▓рди рдХреЗ рд▓рд┐рдП рдХрднреА-рдХрднреА рдРрд╕реЗ рдмрд▓рд┐рджрд╛рдиреЛрдВ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред

рд╣рдо _mm_stream рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдмрдЪрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкрд╛рд╕ рдХрд░рддреЗ рд╣реИрдВ


рд╡рд┐рднрд┐рдиреНрди рдЕрдиреБрдХреВрд▓рди рдЧрд╛рдЗрдб рдПрдХ рдмрд╛рд░ рдлрд┐рд░ рд╕реЗ рдмрд▓реНрдХ рд╕реЗрд╡ рдСрдкрд░реЗрд╢рдВрд╕ рдХреЗ рд▓рд┐рдП рдХреИрд╢ рдХреЛ рдЦреАрдВрдЪрдиреЗ рдХреА рд╕рд▓рд╛рд╣ рдирд╣реАрдВ рджреЗрддреЗ рд╣реИрдВред рдпрд╣ рдЖрдорддреМрд░ рдкрд░ рдЙрдЪрд┐рдд рд╣реИ рдЬрдм рдЖрдк рд▓рдВрдмрд╡рдд рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдХрд░ рд░рд╣реЗ рд╣реИрдВ рдЬреЛ рд╣рдЬрд╛рд░реЛрдВ рдпрд╛ рдЕрдзрд┐рдХ рд╣реИрдВред рд▓реЗрдХрд┐рди рдореИрдЯреНрд░рд┐рд╕ рдХреЗ рд▓рд┐рдП, рдпрд╣ рд╢рд╛рдпрдж рдЗрддрдирд╛ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдирд╣реАрдВ рд╣реИред рд╣рд╛рд▓рд╛рдБрдХрд┐, рдореИрдВ рдЗрд╕реЗ рд╡реИрд╕реЗ рднреА рдЬреЛрдбрд╝реВрдВрдЧрд╛ред

рд╕реНрдЯреНрд░реАрдо рд╕реЗрд╡рд┐рдВрдЧ рдСрдкреНрд╢рди
 void mul_mtx4_mtx4_sse_v4(__m128* const r, __m128 const* const m, __m128 const* const n) { _mm_stream_ps(&r[0].m128_f32[0], mad(m[0], shuf<3>(n[0]), m[1]*shuf<2>(n[0])) + mad(m[2], shuf<1>(n[0]), m[3]*shuf<0>(n[0]))); _mm_stream_ps(&r[1].m128_f32[0], mad(m[0], shuf<3>(n[1]), m[1]*shuf<2>(n[1])) + mad(m[2], shuf<1>(n[1]), m[3]*shuf<0>(n[1]))); _mm_stream_ps(&r[2].m128_f32[0], mad(m[0], shuf<3>(n[2]), m[1]*shuf<2>(n[2])) + mad(m[2], shuf<1>(n[2]), m[3]*shuf<0>(n[2]))); _mm_stream_ps(&r[3].m128_f32[0], mad(m[0], shuf<3>(n[3]), m[1]*shuf<2>(n[3])) + mad(m[2], shuf<1>(n[3]), m[3]*shuf<0>(n[3]))); } 


рдпрд╣рд╛рдВ рд╕рдордп рдореЗрдВ рдХреБрдЫ рднреА рдирд╣реАрдВ рдмрджрд▓рд╛ рд╣реИ, рд╢рдмреНрдж рд╕реЗ рдмрд┐рд▓реНрдХреБрд▓ рднреА рдирд╣реАрдВред рд▓реЗрдХрд┐рди, рд╕рд┐рдлрд╛рд░рд┐рд╢реЛрдВ рдХреЗ рдЕрдиреБрд╕рд╛рд░, рд╣рдо рдЕрдм рдХреИрд╢ рдХреЛ рдПрдХ рдмрд╛рд░ рдлрд┐рд░ рд╕реЗ рдирд╣реАрдВ рдЫреВрддреЗ рд╣реИрдВред

рдПрд╡реАрдПрдХреНрд╕ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди


рдЖрдзрд╛рд░ AVX рд╡рд┐рдХрд▓реНрдк



рдЕрдЧрд▓рд╛, рд╣рдо рдЕрдиреБрдХреВрд▓рди рдХреЗ рдЕрдЧрд▓реЗ рдЪрд░рдг рдкрд░ рдЬрд╛рддреЗ рд╣реИрдВред 4 рд╡реЗрдВ рдлреНрд▓реЛрдЯ рдХреЛ рдПрд╕рдПрд╕рдИ рд░рдЬрд┐рд╕реНрдЯрд░ рдореЗрдВ рд╢рд╛рдорд┐рд▓ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рдФрд░ рдПрд╡реАрдПрдХреНрд╕ рдореЗрдВ рдпрд╣ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА 8. рд╣реИред рдЕрд░реНрдерд╛рддреН, рдкреНрд░рджрд░реНрд╢рди рдХрд┐рдП рдЧрдП рд╕рдВрдЪрд╛рд▓рди рдХреА рд╕рдВрдЦреНрдпрд╛ рдХреЛ рдХрдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рд╕реИрджреНрдзрд╛рдВрддрд┐рдХ рдореМрдХрд╛ рд╣реИ, рдФрд░ рдЙрддреНрдкрд╛рджрдХрддрд╛ рдореЗрдВ рд╡реГрджреНрдзрд┐ рдЕрдЧрд░ рдЖрдзреЗ рд╕реЗ рдирд╣реАрдВ, рддреЛ рдХрдо рд╕реЗ рдХрдо 1.5 рдЧреБрдирд╛ред рд▓реЗрдХрд┐рди рдХреБрдЫ рдореБрдЭреЗ рдмрддрд╛рддрд╛ рд╣реИ рдХрд┐ рдПрд╡реАрдПрдХреНрд╕ рдХреЗ рд╕рдВрдХреНрд░рдордг рдХреЗ рд╕рд╛рде рд╕рдм рдХреБрдЫ рдЗрддрдирд╛ рд╕рд░рд▓ рдирд╣реАрдВ рд╣реЛрдЧрд╛ред рдХреНрдпрд╛ рд╣рдо рдбрдмрд▓ рд░рдЬрд┐рд╕реНрдЯрд░ рд╕реЗ рдЖрд╡рд╢реНрдпрдХ рдбреЗрдЯрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ?

рдЖрдЗрдП рдЗрд╕реЗ рдЬрд╛рдирдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░рддреЗ рд╣реИрдВред рдлрд┐рд░, рд╣рдо рдЕрдкрдиреЗ рдЧреБрдгрди рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХрд╛ рдЙрдкрдпреЛрдЧ рдКрдкрд░ рд▓рд┐рдЦрддреЗ рд╣реИрдВред рдЖрдк рдРрд╕рд╛ рдирд╣реАрдВ рдХрд░ рд╕рдХрддреЗ, рд▓реЗрдХрд┐рди рдЬрдм рд╕рдм рдХреБрдЫ рдкрд╛рд╕ рд╣реЛ рддреЛ рдХреЛрдб рд╕реЗ рдирд┐рдкрдЯрдирд╛ рдЕрдзрд┐рдХ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд╣реЛрдЧрд╛ рдФрд░ рдЖрдкрдХреЛ рдЖрдзреЗ рдкреГрд╖реНрда рддрдХ рд╕реНрдХреНрд░реЙрд▓ рдирд╣реАрдВ рдХрд░рдирд╛ рдкрдбрд╝реЗрдЧрд╛ред

 //    : 00, 10, 20, 30, 01, 11, 21, 31, 02, 12, 22, 32, 03, 13, 23, 33 //   SSE: r0 = m0*n00 + m1*n10 + m2*n20 + m3*n30 r1 = m0*n01 + m1*n11 + m2*n21 + m3*n31 r2 = m0*n02 + m1*n12 + m2*n22 + m3*n32 r3 = m0*n03 + m1*n13 + m2*n23 + m3*n33 

рдЖрдЙрдЯрдкреБрдЯ рдкрд░, рд╣рдо ymm = {r0: r1} рдФрд░ ymm = {r2: r3} рдореЗрдВ рдкрд░рд┐рдгрд╛рдо рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреА рдЙрдореНрдореАрдж рдХрд░рддреЗ рд╣реИрдВред рдпрджрд┐ SSE рд╕рдВрд╕реНрдХрд░рдг рдореЗрдВ рд╣рдорд╛рд░реЗ рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХреЛ рд╕реНрддрдВрднреЛрдВ рдХреЗ рд▓рд┐рдП рд╕рд╛рдорд╛рдиреНрдпреАрдХреГрдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛, рддреЛ рдЕрдм рд╣рдореЗрдВ рдЗрд╕реЗ рдкрдВрдХреНрддрд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП рд╕рд╛рдорд╛рдиреНрдпреАрдХреГрдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рддреЛ рдПрд╕рдПрд╕рдИ рд╡рд┐рдХрд▓реНрдк рдХреЗ рдорд╛рдорд▓реЗ рдореЗрдВ рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд╛рдо рдирд╣реАрдВ рдХрд░реЗрдЧрд╛ред

рдпрджрд┐ рд╣рдо рд░рдЬрд┐рд╕реНрдЯрд░реЛрдВ ymm рдореЗрдВ рдореИрдЯреНрд░рд┐рдХреНрд╕ m рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рд╣рдо рдХреНрд░рдорд╢рдГ ymm = {m0: m1} рдФрд░ ymm = {m2: m3} рдкреНрд░рд╛рдкреНрдд рдХрд░рддреЗ рд╣реИрдВред рдкрд╣рд▓реЗ, рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рд░рдЬрд┐рд╕реНрдЯрд░ рдореЗрдВ рдХреЗрд╡рд▓ рдореИрдЯреНрд░рд┐рдХреНрд╕ рдХреЙрд▓рдо рдереЗ, рдФрд░ рдЕрдм рдХреЙрд▓рдо рдФрд░ рдкрдВрдХреНрддрд┐рдпрд╛рдБ рд╣реИрдВред

рдпрджрд┐ рдЖрдк рдкрд╣рд▓реЗ рдХреА рддрд░рд╣ рдХрд╛рд░реНрдп рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рдЖрдкрдХреЛ ymm = {m0: m1} рдХреЛ рд░рдЬрд┐рд╕реНрдЯрд░ ymm = {n00, n00, n00, n00} рд╕реЗ рдЧреБрдгрд╛ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ : {n10, n10, n10, n10} ред рдЪреВрдВрдХрд┐ n00 рдФрд░ n01 рдореИрдЯреНрд░рд┐рдХреНрд╕ n рдХреА рдПрдХ рд╣реА рдкрдВрдХреНрддрд┐ рдореЗрдВ рд╣реИрдВ, рдПрд╡реАрдПрдХреНрд╕ рдирд┐рд░реНрджреЗрд╢реЛрдВ рдХреЗ рдЙрдкрд▓рдмреНрдз рд╕реЗрдЯ рдХреЛ рджреЗрдЦрддреЗ рд╣реБрдП, рдЙрдиреНрд╣реЗрдВ рдпрдо рджреНрд╡рд╛рд░рд╛ рдмрд┐рдЦреЗрд░рдирд╛ рдорд╣рдВрдЧрд╛ рд╣реЛрдЧрд╛ред рд╢рдлрд▓ рдФрд░ рдкрд░рдорд┐рдЯ рджреЛрдиреЛрдВ рдпрдо рд░рдЬрд┐рд╕реНрдЯрд░реЛрдВ рдХреЗ рдЕрдВрджрд░ рдПрдХ рдлреНрд▓реЛрдЯ (рдЙрдЪреНрдЪ рдФрд░ рдирд┐рдореНрди рдПрдХреНрд╕рдПрдордПрдо ) рдХреЗ рджреЛ рдЪрд╛рд░ рдореЗрдВ рд╕реЗ рдкреНрд░рддреНрдпреЗрдХ рдХреЗ рд▓рд┐рдП рдЕрд▓рдЧ рд╕реЗ рдХрд╛рдо рдХрд░рддреЗ рд╣реИрдВред

рдпрджрд┐ рд╣рдо рдореИрдЯреНрд░рд┐рдХреНрд╕ n рд╕реЗ ymm рд▓реЗрддреЗ рд╣реИрдВ, рддреЛ рд╣рдо рджреЛрдиреЛрдВ рддрддреНрд╡реЛрдВ n00 рдФрд░ n10 рдХреЛ ymm рд░рдЬрд┐рд╕реНрдЯрд░ рдХреЗ рдЕрдВрджрд░ рдЕрдзрд┐рдХрддрдо 2 xmm рдореЗрдВ рдкреНрд░рд╛рдкреНрдд рдХрд░рддреЗ рд╣реИрдВред {n00, n10, n20, n30}: {n01, n11, n21, n31} ред рдЖрдорддреМрд░ рдкрд░, рдореМрдЬреВрджрд╛ рдирд┐рд░реНрджреЗрд╢реЛрдВ рдХреЗ рд▓рд┐рдП рд╕реВрдЪрдХрд╛рдВрдХ 0 рд╕реЗ 3 рддрдХ рд╣реЛрддрд╛ рд╣реИред рдФрд░ рдпрд╣ рдкрддрд╛ рдЪрд▓рддрд╛ рд╣реИ рдХрд┐ рдХреЗрд╡рд▓ рдПрдХ xmm рд░рдЬрд┐рд╕реНрдЯрд░ рдХреЗ рдЕрдВрджрд░ рджреЛ ymm рд░рдЬрд┐рд╕реНрдЯрд░ рдореЗрдВ рд╕реЗ рддреИрд░рддрд╛ рд╣реИ ред рд╕рд╕реНрддреЗ рдореЗрдВ рдкреБрд░рд╛рдиреЗ xmm рд╕реЗ n10 рдХреЛ рд╕рд╕реНрддреЗ рдореЗрдВ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рдирд╛ рд╕рдВрднрд╡ рдирд╣реАрдВ рд╣реИред рдФрд░ рдлрд┐рд░ рдЗрд╕ рдлреЛрдХрд╕ рдХреЛ рдХрдИ рдмрд╛рд░ рджреЛрд╣рд░рд╛рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред рд╣рдо рдЗрд╕ рддрд░рд╣ рдХреЗ рдиреБрдХрд╕рд╛рди рд╕реЗ рдирд╣реАрдВ рдЬреВрдЭ рд╕рдХрддреЗред рдХреБрдЫ рдФрд░ рдХреЗ рд╕рд╛рде рдЖрдирд╛ рдЬрд░реВрд░реА рд╣реИред

рд╣рдо рд╕реНрддрдВрднреЛрдВ рдХреЛ рд╕рд╛рдорд╛рдиреНрдп рдХрд░рддреЗ рдереЗ, рд▓реЗрдХрд┐рди рдЕрдм рдкрдВрдХреНрддрд┐рдпреЛрдВ рдХреЛред рдЗрд╕рд▓рд┐рдП, рд╣рдо рдереЛрдбрд╝рд╛ рдЕрд▓рдЧ рддрд░реАрдХреЗ рд╕реЗ рдЬрд╛рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░реЗрдВрдЧреЗред рд╣рдореЗрдВ {r0: r1} рдореЗрдВ рдкрд░рд┐рдгрд╛рдо рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдЗрд╕рдХрд╛ рдорддрд▓рдм рдпрд╣ рд╣реИ рдХрд┐ рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХреЛ рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХреА рдЕрд▓рдЧ-рдЕрд▓рдЧ рд▓рд╛рдЗрдиреЛрдВ рдореЗрдВ рдирд╣реАрдВ, рдмрд▓реНрдХрд┐ рдПрдХ рд╕рд╛рде рджреЛ рдореЗрдВ рд╕реБрдзрд╛рд░ рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред рдФрд░ рдпрд╣рд╛рдБ, рдХреНрдпрд╛ рдлреЗрд░рдмрджрд▓ рдФрд░ рдкрд░рдорд┐рдЯ рдХреЗ рдХрд╛рдо рдореЗрдВ рдПрдХ рд╢реВрдиреНрдп рдерд╛ рдЬреЛ рд╣рдорд╛рд░реЗ рд▓рд┐рдП рдПрдХ рдкреНрд▓рд╕ рд╣реЛрдЧрд╛ред рд╣рдо рджреЗрдЦрддреЗ рд╣реИрдВ рдХрд┐ рдЬрдм рд╣рдо рдореИрдЯреНрд░рд┐рдХреНрд╕ рдПрди рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░рддреЗ рд╣реИрдВ рддреЛ рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдпрдо рд░рдЬрд┐рд╕реНрдЯрд░реЛрдВ рдореЗрдВ рдХреНрдпрд╛ рд╣реЛрдЧрд╛ред

 n0n1 = {00, 10, 20, 30} : {01, 11, 21, 31} n2n3 = {02, 12, 22, 32} : {03, 13, 23, 33} 

рд╣рд╛рдБ, рд╣рдо рджреЗрдЦрддреЗ рд╣реИрдВ рдХрд┐ ymm register рдХреЗ рд╡рд┐рднрд┐рдиреНрди xmm рднрд╛рдЧреЛрдВ рдореЗрдВ рд╣рдорд╛рд░реЗ рдкрд╛рд╕ 00 рдФрд░ 01 рддрддреНрд╡ рд╣реИрдВред рдЙрдиреНрд╣реЗрдВ {_00, _00, _00, _00} рдореЗрдВ рдкрд░рдорд┐рдЯ рдХрдорд╛рдВрдб рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд░рдЬрд┐рд╕реНрдЯрд░ рдХрд░рдХреЗ рдЧреБрдгрд╛ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ : {_ 01, _01, _01, _01} , рджреЛрдиреЛрдВ xmm рднрд╛рдЧреЛрдВ рдХреЗ рд▓рд┐рдП рдХреЗрд╡рд▓ рдПрдХ рдЗрдВрдбреЗрдХреНрд╕ 3 рдХреЛ рджрд░реНрд╢рд╛рддрд╛ рд╣реИ ред рдпрд╣ рд╡рд╣реА рд╣реИ рдЬреЛ рд╣рдореЗрдВ рдЪрд╛рд╣рд┐рдПред рджрд░рдЕрд╕рд▓, рдЧреБрдгрд╛рдВрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рд╡рд┐рднрд┐рдиреНрди рд▓рд╛рдЗрдиреЛрдВ рдореЗрдВ рднреА рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЧреБрдгрди рдХреЗ рд▓рд┐рдП рд╕рдВрдмрдВрдзрд┐рдд рдпрдо рд░рдЬрд┐рд╕реНрдЯрд░ рдореЗрдВ рдЕрдм рдХреЗрд╡рд▓ {m0: m0} , рдпрд╛рдиреА рдореИрдЯреНрд░рд┐рдХреНрд╕ m рдХреА рдбреБрдкреНрд▓реАрдХреЗрдЯреЗрдб рдкрд╣рд▓реА рдкрдВрдХреНрддрд┐ рдХреЛ рд░рдЦрдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реЛрдЧрд╛ред

рдЗрд╕рд▓рд┐рдП, рд╣рдо рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХреЛ рдЕрдзрд┐рдХ рд╡рд┐рд╕реНрддрд╛рд░ рд╕реЗ рдЪрд┐рддреНрд░рд┐рдд рдХрд░рддреЗ рд╣реИрдВред рд╣рдо ymm рд░рдЬрд┐рд╕реНрдЯрд░реЛрдВ рдореЗрдВ рдореИрдЯреНрд░рд┐рдХреНрд╕ m рдХреА рджреЛрд╣рд░реА рдкрдВрдХреНрддрд┐рдпреЛрдВ рдХреЛ рдкрдврд╝рддреЗ рд╣реИрдВ:

 mm[0] = {m0:m0} mm[1] = {m1:m1} mm[2] = {m2:m2} mm[3] = {m3:m3} 

рдФрд░ рдлрд┐рд░ рд╣рдо рдЧреБрдгрд╛ рдХреА рдЧрдгрдирд╛ рдХрд░реЗрдВрдЧреЗ:

 r0r1 = mm[0] * {n00,n00,n00,n00:n01,n01,n01,n01} + // permute<3,3,3,3>(n0n1) mm[1] * {n10,n10,n10,n10:n11,n11,n11,n11} + // permute<2,2,2,2>(n0n1) mm[2] * {n20,n20,n20,n20:n21,n21,n21,n21} + // permute<1,1,1,1>(n0n1) mm[3] * {n30,n30,n30,n30:n31,n31,n31,n31} // permute<0,0,0,0>(n0n1) r2r3 = mm[0] * {n02,n02,n02,n02:n03,n03,n03,n03} + // permute<3,3,3,3>(n2n3) mm[1] * {n12,n12,n12,n12:n13,n13,n13,n13} + // permute<2,2,2,2>(n2n3) mm[2] * {n22,n22,n22,n22:n23,n23,n23,n23} + // permute<1,1,1,1>(n2n3) mm[3] * {n32,n32,n32,n32:n33,n33,n33,n33} // permute<0,0,0,0>(n2n3) 

рд╣рдо рдФрд░ рдЕрдзрд┐рдХ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рд▓рд┐рдЦрддреЗ рд╣реИрдВ:

 r0r1 = mm[0]*n0n1<3,3,3,3>+mm[1]*n0n1<2,2,2,2>+mm[2]*n0n1<1,1,1,1>+mm[3]*n0n1<0,0,0,0> r2r3 = mm[0]*n2n3<3,3,3,3>+mm[1]*n2n3<2,2,2,2>+mm[2]*n2n3<1,1,1,1>+mm[3]*n2n3<0,0,0,0> 

рдпрд╛ рд╕рд░рд▓реАрдХреГрдд рд░реВрдк рдореЗрдВ:

 r0r1 = mm[0]*n0n1<3> + mm[1]*n0n1<2> + mm[2]*n0n1<1> + mm[3]*n0n1<0> r2r3 = mm[0]*n2n3<3> + mm[1]*n2n3<2> + mm[2]*n2n3<1> + mm[3]*n2n3<0> 

рд╕рдм рдХреБрдЫ рд╕реНрдкрд╖реНрдЯ рд╣реЛрдиреЗ рд▓рдЧрддрд╛ рд╣реИред

рдпрд╣ рдХреЗрд╡рд▓ рдПрдХ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд▓рд┐рдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдмрдиреА рд╣реБрдИ рд╣реИ
 void mul_mtx4_mtx4_avx_v1(__m128* const r, __m128 const* const m, __m128 const* const n) { __m256 mm0 = _mm256_set_m128(m[0], m[0]); __m256 mm1 = _mm256_set_m128(m[1], m[1]); __m256 mm2 = _mm256_set_m128(m[2], m[2]); __m256 mm3 = _mm256_set_m128(m[3], m[3]); __m256 n0n1 = _mm256_load_ps(&n[0].m128_f32[0]); __m256 y1 = _mm256_permute_ps(n0n1, 0xFF);//3,3,3,3 __m256 y2 = _mm256_permute_ps(n0n1, 0xAA);//2,2,2,2 __m256 y3 = _mm256_permute_ps(n0n1, 0x55);//1,1,1,1 __m256 y4 = _mm256_permute_ps(n0n1, 0x00);//0,0,0,0 y1 = _mm256_mul_ps(y1, mm0); y2 = _mm256_mul_ps(y2, mm1); y3 = _mm256_mul_ps(y3, mm2); y4 = _mm256_mul_ps(y4, mm3); y1 = _mm256_add_ps(y1, y2); y3 = _mm256_add_ps(y3, y4); y1 = _mm256_add_ps(y1, y3); __m256 n2n3 = _mm256_load_ps(&n[2].m128_f32[0]); __m256 y5 = _mm256_permute_ps(n2n3, 0xFF); __m256 y6 = _mm256_permute_ps(n2n3, 0xAA); __m256 y7 = _mm256_permute_ps(n2n3, 0x55); __m256 y8 = _mm256_permute_ps(n2n3, 0x00); y5 = _mm256_mul_ps(y5, mm0); y6 = _mm256_mul_ps(y6, mm1); y7 = _mm256_mul_ps(y7, mm2); y8 = _mm256_mul_ps(y8, mm3); y5 = _mm256_add_ps(y5, y6); y7 = _mm256_add_ps(y7, y8); y5 = _mm256_add_ps(y5, y7); _mm256_stream_ps(&r[0].m128_f32[0], y1); _mm256_stream_ps(&r[2].m128_f32[0], y5); } 


рдпрд╣рд╛рдБ IACA рд╕реЗ рд░реЛрдЪрдХ рд╕рдВрдЦреНрдпрд╛рдПрдБ рд╣реИрдВ: x86 - 12.53, x64 - 12 ред рд╣рд╛рд▓рд╛рдВрдХрд┐, рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ, рдореИрдВ рдмреЗрд╣рддрд░ рдЪрд╛рд╣рддрд╛ рдерд╛ред рдХреБрдЫ рдпрд╛рдж рдЖрдпрд╛ред

рдПрд╡реАрдПрдХреНрд╕ рдСрдкреНрдЯрд┐рдорд╛рдЗрдЬрд╝реЗрд╢рди рдкреНрд▓рд╕ рд╕рд┐рдВрдереИрдЯрд┐рдХ рд╢реБрдЧрд░


рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЙрдкрд░реЛрдХреНрдд рдХреЛрдб рдореЗрдВ, AVX рдХреЛ рдЗрд╕рдХреА рдкреВрд░реА рдХреНрд╖рдорддрд╛ рдХреЗ рд▓рд┐рдП рдЗрд╕реНрддреЗрдорд╛рд▓ рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ред рд╣рдо рдкрд╛рддреЗ рд╣реИрдВ рдХрд┐ рдпрдо рд░рдЬрд┐рд╕реНрдЯрд░ рдореЗрдВ рджреЛ рд╕рдорд╛рди рд░реЗрдЦрд╛рдПрдБ рд╕реЗрдЯ рдХрд░рдиреЗ рдХреЗ рдмрдЬрд╛рдп, рд╣рдо рдкреНрд░рд╕рд╛рд░рдг рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдЬреЛ рджреЛ рд╕рдорд╛рди рдорд┐рдореА рдорд╛рдиреЛрдВ рдХреЗ рд╕рд╛рде рдпрдо рд░рдЬрд┐рд╕реНрдЯрд░ рднрд░ рд╕рдХрддреЗ рд╣реИрдВред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рд░рд╛рд╕реНрддреЗ рдореЗрдВ, AVX рдлрд╝рдВрдХреНрд╢рди рдХреЗ рд▓рд┐рдП рдХреБрдЫ "рд╕рд┐рдВрдЯреИрдХреНрдЯрд┐рдХ рд╢реБрдЧрд░" рдЬреЛрдбрд╝реЗрдВред

рдЙрдиреНрдирдд AVX рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди
 __m256 operator + (__m256 const a, __m256 const b) { return _mm256_add_ps(a, b); } __m256 operator - (__m256 const a, __m256 const b) { return _mm256_sub_ps(a, b); } __m256 operator * (__m256 const a, __m256 const b) { return _mm256_mul_ps(a, b); } __m256 operator / (__m256 const a, __m256 const b) { return _mm256_div_ps(a, b); } template <int i> __m256 perm(__m256 const v) { return _mm256_permute_ps(v, _MM_SHUFFLE(i, i, i, i)); } template <int a, int b, int c, int d> __m256 perm(__m256 const v) { return _mm256_permute_ps(v, _MM_SHUFFLE(a, b, c, d)); } template <int i, int j> __m256 perm(__m256 const v) { return _mm256_permutevar_ps(v, _mm256_set_epi32(i, i, i, i, j, j, j, j)); } template <int a, int b, int c, int d, int e, int f, int g, int h> __m256 perm(__m256 const v) { return _mm256_permutevar_ps(v, _mm256_set_epi32(a, b, c, d, e, f, g, h)); } __m256 mad(__m256 const a, __m256 const b, __m256 const c) { return _mm256_add_ps(_mm256_mul_ps(a, b), c); } void mul_mtx4_mtx4_avx_v2(__m128* const r, __m128 const* const m, __m128 const* const n) { __m256 const mm[] { _mm256_broadcast_ps(m+0), _mm256_broadcast_ps(m+1), _mm256_broadcast_ps(m+2), _mm256_broadcast_ps(m+3) }; __m256 const n0n1 = _mm256_load_ps(&n[0].m128_f32[0]); _mm256_stream_ps(&r[0].m128_f32[0], mad(perm<3>(n0n1), mm[0], perm<2>(n0n1)*mm[1])+ mad(perm<1>(n0n1), mm[2], perm<0>(n0n1)*mm[3])); __m256 const n2n3 = _mm256_load_ps(&n[2].m128_f32[0]); _mm256_stream_ps(&r[2].m128_f32[0], mad(perm<3>(n2n3), mm[0], perm<2>(n2n3)*mm[1])+ mad(perm<1>(n2n3), mm[2], perm<0>(n2n3)*mm[3])); } 


рдФрд░ рдпрд╣рд╛рдВ рдкрд░рд┐рдгрд╛рдо рдкрд╣рд▓реЗ рд╕реЗ рдЕрдзрд┐рдХ рджрд┐рд▓рдЪрд╕реНрдк рд╣реИрдВред IACA рд╕рдВрдЦреНрдпрд╛рдУрдВ рдХрд╛ рдЙрддреНрдкрд╛рджрди рдХрд░рддрд╛ рд╣реИ: x86 - 10, x64 - 8.58 , рдЬреЛ рдмрд╣реБрдд рдмреЗрд╣рддрд░ рджрд┐рдЦрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдлрд┐рд░ рднреА 2 рдмрд╛рд░ рдирд╣реАрдВред

AVX + FMA рд╡рд┐рдХрд▓реНрдк (рдЕрдВрддрд┐рдо)


рдЪрд▓рд┐рдП рдПрдХ рдФрд░ рдкреНрд░рдпрд╛рд╕ рдХрд░рддреЗ рд╣реИрдВред рдЕрдм рдПрдПрдордПрдХреНрд╕ рдХреЗ рдмрд╛рдж рдЗрд╕реЗ рдкреНрд░реЛрд╕реЗрд╕рд░ рдореЗрдВ рдЬреЛрдбрд╝рд╛ рдЧрдпрд╛ рдерд╛, рдлрд┐рд░ рд╕реЗ рдПрдлрдПрдордП рдирд┐рд░реНрджреЗрд╢ рдХреЛ рдлрд┐рд░ рд╕реЗ рдпрд╛рдж рдХрд░рдирд╛ рддрд░реНрдХрд╕рдВрдЧрдд рд╣реЛрдЧрд╛ред рдмрд╕ рдЕрд▓рдЧ-рдЕрд▓рдЧ mul + рдХреЛ рдПрдХ рдСрдкрд░реЗрд╢рди рдХреЗ рд▓рд┐рдП рдЬреЛрдбрд╝реЗрдВ ред рдпрджреНрдпрдкрд┐ рд╣рдо рдЕрднреА рднреА рдХрдВрдкрд╛рдЗрд▓рд░ рдХреЛ рдЕрдиреБрдХреВрд▓рди рдХреЗ рд▓рд┐рдП рдЕрдзрд┐рдХ рдЕрд╡рд╕рд░ рджреЗрдиреЗ рдХреЗ рд▓рд┐рдП рдЧреБрдгрди рдХреЗ рдирд┐рд░реНрджреЗрд╢ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ, рдФрд░ рдкреНрд░реЛрд╕реЗрд╕рд░ рдЧреБрдгрди рдХреЗ рд╕рдорд╛рдирд╛рдВрддрд░ рдирд┐рд╖реНрдкрд╛рджрди рдХреЗ рд▓рд┐рдПред рдЖрдорддреМрд░ рдкрд░ рдореИрдВ рдЕрд╕реЗрдВрдмрд▓рд░ рдореЗрдВ рдЙрддреНрдкрдиреНрди рдХреЛрдб рдХреЛ рдпрд╣ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рджреЗрдЦрддрд╛ рд╣реВрдВ рдХрд┐ рдХреМрди рд╕рд╛ рд╡рд┐рдХрд▓реНрдк рдмреЗрд╣рддрд░ рд╣реИред

рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рд╣рдореЗрдВ рдПрдХ * рдмреА + рд╕реА * рдбреА + рдИ * рдПрдл + рдЬреА * рдПрдЪ рдХреА рдЧрдгрдирд╛ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдЖрдк рдпрд╣ рдорд╛рдерд╛ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ: fma (a, b, fma (c, d, fma (e, f, g * h))) ред рд▓реЗрдХрд┐рди, рдЬреИрд╕рд╛ рдХрд┐ рд╣рдо рджреЗрдЦрддреЗ рд╣реИрдВ, рдкрд┐рдЫрд▓реЗ рдПрдХ рдХреЛ рдкреВрд░рд╛ рдХрд┐рдП рдмрд┐рдирд╛ рдпрд╣рд╛рдВ рдПрдХ рдСрдкрд░реЗрд╢рди рдХрд░рдирд╛ рдЕрд╕рдВрднрд╡ рд╣реИред рдФрд░ рдЗрд╕рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рд╣рдо рдпреБрдЧреНрдорд┐рдд рдЧреБрдгрд╛ рдХрд░рдиреЗ рдХреА рдХреНрд╖рдорддрд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд░ рдкрд╛рдПрдВрдЧреЗ, рдХреНрдпреЛрдВрдХрд┐ SIMD рдкрд╛рдЗрдкрд▓рд╛рдЗрди рд╣рдореЗрдВ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддреА рд╣реИред рдпрджрд┐ рд╣рдо рдЧрдгрдирд╛рдУрдВ рдХреЛ рдереЛрдбрд╝рд╛ рдмрджрд▓ рджреЗрддреЗ рд╣реИрдВ рддреЛ fma (a, b, c * d) + fma (e, f, g * h) , рд╣рдо рджреЗрдЦреЗрдВрдЧреЗ рдХрд┐ рд╣рдо рдЧрдгрдирд╛ рдХреЛ рд╕рдорд╛рдирд╛рдВрддрд░ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдкрд╣рд▓реЗ рджреЛ рд╕реНрд╡рддрдВрддреНрд░ рдЧреБрдгрди рдХрд░рддреЗ рд╣реИрдВ, рдФрд░ рдлрд┐рд░ рджреЛ рд╕реНрд╡рддрдВрддреНрд░ fma рд╕рдВрдЪрд╛рд▓рди рдХрд░рддреЗ рд╣реИрдВред

рдПрд╡реАрдПрдХреНрд╕ + рдПрдлрдПрдордП рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди
 __m256 fma(__m256 const a, __m256 const b, __m256 const c) { return _mm256_fmadd_ps(a, b, c); } void mul_mtx4_mtx4_avx_fma(__m128* const r, __m128 const* const m, __m128 const* const n) { __m256 const mm[]{ _mm256_broadcast_ps(m + 0), _mm256_broadcast_ps(m + 1), _mm256_broadcast_ps(m + 2), _mm256_broadcast_ps(m + 3) }; __m256 const n0n1 = _mm256_load_ps(&n[0].m128_f32[0]); _mm256_stream_ps(&r[0].m128_f32[0], fma(perm<3>(n0n1), mm[0], perm<2>(n0n1)*mm[1])+ fma(perm<1>(n0n1), mm[2], perm<0>(n0n1)*mm[3])); __m256 const n2n3 = _mm256_load_ps(&n[2].m128_f32[0]); _mm256_stream_ps(&r[2].m128_f32[0], fma(perm<3>(n2n3), mm[0], perm<2>(n2n3)*mm[1])+ fma(perm<1>(n2n3), mm[2], perm<0>(n2n3)*mm[3])); } 


IACA: x86 - 9.21, x64 - 8 ред рдЕрдм рдпрд╣ рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рд╣реИ рдХреЛрдИ рд╢рд╛рдпрдж рдХрд╣реЗрдЧрд╛ рдХрд┐ рдХреНрдпрд╛ рдмреЗрд╣рддрд░ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдореИрдВ рдирд╣реАрдВ рдЬрд╛рдирддрд╛ рдХрд┐ рдХреИрд╕реЗред

рдорд╛рдирдХ


рдореИрдВ рддреБрд░рдВрдд рдзреНрдпрд╛рди рджреЗрддрд╛ рд╣реВрдВ рдХрд┐ рдЗрди рдЖрдВрдХрдбрд╝реЛрдВ рдХреЛ рдЕрдВрддрд┐рдо рд╕рддреНрдп рдХреЗ рд░реВрдк рдореЗрдВ рдирд╣реАрдВ рд▓рд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред рдПрдХ рдирд┐рд╢реНрдЪрд┐рдд рдкрд░реАрдХреНрд╖рдг рдХреЗ рд╕рд╛рде рднреА, рд╡реЗ рдХреБрдЫ рд╕реАрдорд╛рдУрдВ рдХреЗ рднреАрддрд░ рддреИрд░рддреЗ рд╣реИрдВредрдФрд░ рдЗрд╕рд╕реЗ рднреА рдЕрдзрд┐рдХ рд╡реЗ рд╡рд┐рднрд┐рдиреНрди рдкреНрд▓реЗрдЯрдлрд╛рд░реНрдореЛрдВ рдкрд░ рдЕрд▓рдЧ рддрд░рд╣ рд╕реЗ рд╡реНрдпрд╡рд╣рд╛рд░ рдХрд░рддреЗ рд╣реИрдВред рдХрд┐рд╕реА рднреА рдЕрдиреБрдХреВрд▓рди рдХреЗ рд╕рд╛рде, рдЕрдкрдиреЗ рдорд╛рдорд▓реЗ рдХреЗ рд▓рд┐рдП рд╡рд┐рд╢реЗрд╖ рд░реВрдк рд╕реЗ рдорд╛рдк рд▓реЗрдВред

рд╕рд╛рдордЧреНрд░реА рдХреА рддрд╛рд▓рд┐рдХрд╛


  • рд╕рдорд╛рд░реЛрд╣: рд╕рдорд╛рд░реЛрд╣ рдХрд╛ рдирд╛рдоред рдПрд╕ рдкрд░ рд╕рдорд╛рдкреНрдд - рдПрдХ рд╕реНрдЯреНрд░реАрдорд┐рдВрдЧ рдХреЗ рд╕рд╛рде рдХрд╛рд░реНрдп рдХрд░рддрд╛ рд╣реИ, рдЕрд▓рдЧ-рдЕрд▓рдЧ рд╕рд╛рдорд╛рдиреНрдп Mov (рд╕реНрдЯреНрд░реАрдорд┐рдВрдЧ рдХреЗ рдмрд┐рдирд╛)ред рд╕реНрдкрд╖реНрдЯрддрд╛ рдХреЗ рд▓рд┐рдП рдЬреЛрдбрд╝рд╛ рдЧрдпрд╛, рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдкрд░реНрдпрд╛рдкреНрдд рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИред
  • IACA рдЪрдХреНрд░: IACA рджреНрд╡рд╛рд░рд╛ рдЧрдгрдирд╛ рдХреА рдЧрдИ рдкреНрд░рддрд┐ рдлрд╝рдВрдХреНрд╢рди рдЯрд┐рдХ рдХреА рд╕рдВрдЦреНрдпрд╛
  • рдорд╛рдкрд╛ рдЪрдХреНрд░: рдорд╛рдкреА рдЧрдИ рд╕рдВрдЦреНрдпрд╛ рдХреА рдорд╛рдк (рдХрдо рдЕрдзрд┐рдХ рд╣реИ)
  • IACA рд╕реНрдкреАрдбрдЕрдк: рдПрдХ рд╢реВрдиреНрдп рд░реЗрдЦрд╛ рдореЗрдВ рдЙрдкрд╛рдпреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ / рдПрдХ рдкрдВрдХреНрддрд┐ рдореЗрдВ рдЙрдкрд╛рдпреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛
  • рдорд╛рдкрд╛ рдЧрддрд┐: рд╢реВрдиреНрдп рд▓рд╛рдЗрди рдореЗрдВ рдЙрдкрд╛рдпреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ / рд▓рд╛рдЗрди рдореЗрдВ рдЙрдкрд╛рдпреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ (рдЕрдзрд┐рдХ рдмреЗрд╣рддрд░)


рд▓реВрдк_рдо рдХреЗ рд▓рд┐рдП, рд▓реЗрдЦ рдХреЗ рдЯрд┐рдХреНрд╕ рдХреЛ 64 рд╕реЗ рдЧреБрдгрд╛ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ред рдЕрд░реНрдерд╛рддреН, рдпрд╣ рдПрдХ рдмрд╣реБрдд рд╣реА рдЕрдиреБрдорд╛рдирд┐рдд рдореВрд▓реНрдп рд╣реИред рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рдпрд╣ рдЗрд╕ рддрд░рд╣ рд╕реЗ рдирд┐рдХрд▓рд╛ред

i3-3770:


86
рд╕рдорд╛рд░реЛрд╣IACA рдЪрдХреНрд░рдорд╛рдкрд╛ рдЪрдХреНрд░IACA рд╕реНрдкреАрдбрдЕрдкрдорд╛рдкрд╛ рдЧрдпрд╛ рд╕реНрдкреАрдбрдЕрдк
unroll_m70.0050.751.001.00
loop_m233.60119.210.300.43
sse_v1m18.8927.513.701.84
sse_v2m19.0027.613.681.84
sse_v3m18.8927.223.701.86
sse_v4s18.8927.183.701.87
avx_v1m13.0019.215.382.64
avx_v1s13.0020.035.382.53
avx_v2m10.0012.916.993.93
avx_v2s10.0017.346.992.93

64
FunctionIACA cyclesMeasured cyclesIACA speedupMeasured speedup
unroll_m7068.601.001.00
loop_m233.60119.370.300.57
sse_v1m18.8921.983.703.12
sse_v2m19.0021.093.683.25
sse_v3m18.8922.193.703.09
sse_v4s18.8922.393.703.06
avx_v1m13.009.615.387.13
avx_v1s13.0016.905.384.06
avx_v2m10.009.206.997.45
avx_v2s10.0014.646.994.68

i7-8700K:


86
FunctionIACA cyclesMeasured cyclesIACA speedupMeasured speedup
unroll_m69.9540.251.001.00
loop_m233.6079.490.300.51
sse_v1m18.8919.313.702.09
sse_v2m19.0019.983.682.01
sse_v3m18.8919.693.702.04
sse_v4s18.8919.673.702.05
avx_v1m13.0014.225.382.83
avx_v1s13.0014.135.382.85
avx_v2m10.0011.736.993.43
avx_v2s10.0011.816.993.41
AVX+FMAm9.2110.387.603.88
AVX+FMAs9.2110.327.603.90

64
FunctionIACA cyclesMeasured cyclesIACA speedupMeasured speedup
unroll_m69.9557.111.001.00
loop_m233.6075.730.300.75
sse_v1m18.8915.833.703.61
sse_v2m19.0017.223.683.32
sse_v3m18.8915.923.703.59
sse_v4s18.8916.183.703.53
avx_v1m13.007.035.388.12
avx_v1s13.0012.985.384.40
avx_v2m10.005.406.9910.57
avx_v2s10.0011.396.995.01
AVX+FMAm9.219.737.605.87
AVX+FMAs9.219.817.605.82

рд╕реНрд░реЛрдд рдореЗрдВ рдЯреЗрд╕реНрдЯ рдХреЛрдбред рдпрджрд┐ рдЙрдирдореЗрдВ рд╕реБрдзрд╛рд░ рдХрд░рдиреЗ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЙрдЪрд┐рдд рд╕реБрдЭрд╛рд╡ рд╣реИрдВ, рддреЛ рдЯрд┐рдкреНрдкрдгрд┐рдпреЛрдВ рдореЗрдВ рд▓рд┐рдЦреЗрдВред

рдХрд▓реНрдкрдирд╛ рдХреЗ рджрд╛рдпрд░реЗ рд╕реЗ рдЕрд▓рдЧ


рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рдпрд╣ рдХрд▓реНрдкрдирд╛ рдХреЗ рджрд╛рдпрд░реЗ рд╕реЗ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рдЕрдЧрд░ рдореИрдВрдиреЗ AVX512 рдХреЗ рд▓рд┐рдП рдкреНрд░реЛрд╕реЗрд╕рд░ рджреЗрдЦрд╛, рддреЛ рд╢рд╛рдпрдж рддрд╕реНрд╡реАрд░реЛрдВ рдореЗрдВред рд╣рд╛рд▓рд╛рдВрдХрд┐, рдореИрдВрдиреЗ рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХреАред рдпрд╣рд╛рдВ рдореИрдВ рдХреБрдЫ рднреА рдирд╣реАрдВ рд╕рдордЭрд╛рдКрдВрдЧрд╛, рдПрд╡реАрдПрдХреНрд╕ + рдПрдлрдПрдордП рдХреЗ рд╕рд╛рде рдПрдХ рдкреВрд░реНрдг рд╕рд╛рджреГрд╢реНрдпред рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдПрдХ рд╣реА рд╣реИ, рдХреЗрд╡рд▓ рдХрдо рд╕рдВрдЪрд╛рд▓рдиред

рдЬреИрд╕рд╛ рдХрд┐ рд╡реЗ рдХрд╣рддреЗ рд╣реИрдВ, рдореИрдВ рдЗрд╕реЗ рдпрд╣рд╛рдБ рдЫреЛрдбрд╝ рджреВрдБрдЧрд╛
 __m512 operator + (__m512 const a, __m512 const b) { return _mm512_add_ps(a, b); } __m512 operator - (__m512 const a, __m512 const b) { return _mm512_sub_ps(a, b); } __m512 operator * (__m512 const a, __m512 const b) { return _mm512_mul_ps(a, b); } __m512 operator / (__m512 const a, __m512 const b) { return _mm512_div_ps(a, b); } template <int i> __m512 perm(__m512 const v) { return _mm512_permute_ps(v, _MM_SHUFFLE(i, i, i, i)); } template <int a, int b, int c, int d> __m512 perm(__m512 const v) { return _mm512_permute_ps(v, _MM_SHUFFLE(a, b, c, d)); } __m512 fma(__m512 const a, __m512 const b, __m512 const c) { return _mm512_fmadd_ps(a, b, c); } void mul_mtx4_mtx4_avx512(__m128* const r, __m128 const* const m, __m128 const* const _n) { __m512 const mm[]{ _mm512_broadcast_f32x4(m[0]), _mm512_broadcast_f32x4(m[1]), _mm512_broadcast_f32x4(m[2]), _mm512_broadcast_f32x4(m[3]) }; __m512 const n = _mm512_load_ps(&_n[0].m128_f32[0]); _mm512_stream_ps(&r[0].m128_f32[0], fma(perm<3>(n), mm[0], perm<2>(n)*mm[1])+ fma(perm<1>(n), mm[2], perm<0>(n)*mm[3])); } 


рд╕рдВрдЦреНрдпрд╛ рд╢рд╛рдирджрд╛рд░ рд╣реИрдВ: x86 - 4.79, x64 - 5.42 (SKX рд╡рд╛рд╕реНрддреБрдХрд▓рд╛ рдХреЗ рд╕рд╛рде IACA)ред рдЗрд╕ рддрдереНрдп рдХреЗ рдмрд╛рд╡рдЬреВрдж рдХрд┐ рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдореЗрдВ 64 рдЧреБрдгрд╛ рдФрд░ 48 рдЬреЛрдбрд╝ рд╣реИрдВред

рд▓реЗрдЦ рд╕реЗ PS рдХреЛрдб




рдпрд╣ рдПрдХ рд▓реЗрдЦ рд▓рд┐рдЦрдиреЗ рдХрд╛ рдореЗрд░рд╛ рдкрд╣рд▓рд╛ рдЕрдиреБрднрд╡ рд╣реИред рдЖрдкрдХреА рдЯрд┐рдкреНрдкрдгрд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП рдЖрдк рд╕рднреА рдХрд╛ рдзрдиреНрдпрд╡рд╛рджред рд╡реЗ рдХреЛрдб рдФрд░ рд▓реЗрдЦ рдХреЛ рдмреЗрд╣рддрд░ рдмрдирд╛рдиреЗ рдореЗрдВ рдорджрдж рдХрд░рддреЗ рд╣реИрдВред

Source: https://habr.com/ru/post/hi418247/


All Articles