Еми не е, айде да не си припомняме какво е кеш и колко нива има в съвременните цпу-та.
Ние, конкретно в цитирания пост говорехме за съвсем друго кеширане, нямащо нищо общо с хардуера. Прочети пак.
Джони, ако си беше правил сам базата данни вместо да ползваш sqlite щеше да забравиш неща като "кеширането е един много частен случай", смешното е че ти като майстор асемблерджия би трябвало доста по-добре да ги разбираш работите с кеша но в крайна сметка всички сме ученици и в тая професия.
waldorf
Създадено на 09.03.2023, видяно: 516 пъти. #86837
Моя коментар беше, че скоростта с която се четат данните от паметта е с пъти по бавна от скоростта с която ядрата паралелно циклят в процесора т.е. на едно четене от паметта процесора може да изпълни като нищо 100-на инструкции и при това положение става излишно да оптимизираш на ръка който ще се изпълни на за 20 ами за 10 инструкции - кел файда като процесора така и така трябва да чака докато се вреди за данни. Затова според мен кода генериран от модерен Ц/Ц++ компилатор е достатъчно добър, че да не го дооптимизирам. И по важното става как да си структурираш данните така, че нещата да стават бързо. Отделно, че има неща за които си заслужава да се оптимизира и такива за които не си заслужава - пак е въпрос на субективна преценка.
Това не трябва да се бърка с това на кой програмист кой език му е по лесен и си е свикнал. Джони се оправя на асемблер по добре отколкото болшинството програмисти биха се оправили на език от високо ниво - не виждам никаква причина да го убеждавам да не ползва асемблер. Напротив. Даже му се кефя на постоянството и резултатите. Но за мен избора отдавна е друг - където има ограничения откъм бързодействие, памет и т.н. предпочитам Ц, за всичко друго JS. Но най вече каквото клиента си поръча и плати.
waldorf
Създадено на 09.03.2023, видяно: 515 пъти. #86838
Не бе, говоря за четенето на самите стрингове, че ще ги прочетеш двойно - един път за да видиш колко са дълги и евентуално втори път за да сравниш байт по байт дали са еднакви.
Пак повтарям, за да видя колко са дълги стринговете не ми трябва да ги чета до края. Формата на стринговете, който се използва в StrLib е с префикс в който е записана дължината на стринга (тип Pascal, ако така ти е по-ясно). StrLen определя дължината на стринга за O(1), константно време, без да ги сканира.
Така че, ако стринговете са с различна дължина, цялата операция за сравнение става мигновена, за О(1), въобще без никакво сканиране на стринговете.
А, аз предположих, че работиш с null терминирани стрингове - пълно безумие са ама като са станали стандарт кой да предположи, че ползваш паскалски.
Между другото няма ли как да инлайнваш такива малки функции с макроси? То и това не е панацея защото като се инлайнва кода по често расте и съответно може да има обратен ефект ама има случаи в които за 2-3 инструкции ще извикаш функция с поне още толкова, ще запишеш параметри в стека. Отделно не знам fasm abi-то дали позволява да предаваш параметри в регистри. Те това са все неща които Ц/Ц++ компилатора би направил автоматично за тебе без ти губи времето и особено с LTO (link time optimisations) резултата често е перфектно генериран код.
waldorf
Създадено на 09.03.2023, видяно: 510 пъти. #86840
Заради това, че процесора работи много по бързо отколкото се прочитат данните от паметта (да не говорим за диска) се получава така, че и да си оптимизирал кода си до дупка той ще се изпълни също толкова бавно колкото и по малко оптимизиран от компилатора защото бавното място е другаде.
Това изказване просто не е вярно.
всъщност е абсолютно вярно. софтуера днес е осезаемо по-бавен от преди 20 години защото за 20 години паметта продължи да нараства, но скоростта и - не. отделно преди 20 години софтуера беше офлайн, а днес масово си "приказва" по интернета. основно правило на блоатинга е - ако има ресурс ще се заеме. това прави съвременния софтуер консумиращ повече памет "защото има" от там и забавянето, а не щото не е оптимизиран. виж телефоните как процесора им е по-бавен, ама някак си всичко върви по-мазно от пц
То да е само това. Много платформи са проектирани по времето когато цената на паметта е била с пъти в повече от тази на процесорна инструкция. С времето паметта поевтинява толкова много, че тези стари дизайни започват да стават проблем ама си се ползват по инерция. Например всичките GUI-та.
А, аз предположих, че работиш с null терминирани стрингове - пълно безумие са ама като са станали стандарт кой да предположи, че ползваш паскалски.
Библиотеката е съвместима и с null терминирани стрингове, ако случайно се налага да се използват, примерно върнати от външни библиотеки. Но тъй като това е фолбак, то съответно с такива стрингове бързодействието ще е влошено.
Между другото няма ли как да инлайнваш такива малки функции с макроси? То и това не е панацея защото като се инлайнва кода по често расте и съответно може да има обратен ефект ама има случаи в които за 2-3 инструкции ще извикаш функция с поне още толкова, ще запишеш параметри в стека. Отделно не знам fasm abi-то дали позволява да предаваш параметри в регистри. Те това са все неща които Ц/Ц++ компилатора би направил автоматично за тебе без ти губи времето и особено с LTO (link time optimisations) резултата често е перфектно генериран код.
Разбира се, че има начин, но не обичам да го правя. Лично моето мнение е, че макросите не трябва да генерират повече от 4..5 инструкции изпълним код. Иначе в дългосрочна перспектива създават огромни проблеми и с производителността и с четимостта на кода и с възможностите за поддръжка.
Така че, ако ми трябва нещо да е инлайннато, то просто си го пиша на място. При това имам възможност да си го напиша именно за конкретният частен случай – вида на циклите, използването на регистрите, локалните променливи и т.н.
В асемблера няма понятие като ABI – можеш да си правиш каквото си искаш. Но предаването на параметри в регистрите, когато става въпрос за библиотечни функции не е добра идея. За някакви малки спомагателни функции, да, без проблем.
В асемблера няма понятие като ABI – можеш да си правиш каквото си искаш. Но предаването на параметри в регистрите, когато става въпрос за библиотечни функции не е добра идея. За някакви малки спомагателни функции, да, без проблем.
Обаче нали кода който пишеш трябва да е в PE/ELF формат (за windows/linux) и трябва да правиш syscalls за да заделиш и получиш от OS пойнтер към динамичната памет/heap, т.е. поне при тези OSи си имаш процес и виртуална памет, нямаш достъп до физическата. И като правиш тези syscalls трябва да спазваш ABI конвенциите.
В асемблера няма понятие като ABI – можеш да си правиш каквото си искаш. Но предаването на параметри в регистрите, когато става въпрос за библиотечни функции не е добра идея. За някакви малки спомагателни функции, да, без проблем.
Обаче нали кода който пишеш трябва да е в PE/ELF формат (за windows/linux) и трябва да правиш syscalls за да заделиш и получиш от OS пойнтер към динамичната памет/heap, т.е. поне при тези OSи си имаш процес и виртуална памет, нямаш достъп до физическата. И като правиш тези syscalls трябва да спазваш ABI конвенциите.
Естествено. Но това е ABI-то на операционната система. При Уиндоус е едно, при Линукс съвсем друго.
А вътре в приложението, плюс библиотеките, които сам си пишеш можеш да си използваш каквото си искаш.
Разбира се, човек си изработва някакви правила и "стандарти", но специално за асемблера те са много плаващи и зависят повече от контекста в който се използва някаква конкретна функция.
Аз например масово използвам CF флага за връщане на булеви резултати, просто защото е супер удобно. Имам функции обаче, които връщат булев резултат в ZF.
Имам някои функции, които връщат по няколко резултата в няколко регистъра.
Ако в 90% функциите връщат резултат в EAX, то в някои случаи е по-удобно да се връща резултата примерно в EBX или в EDX, защото предварително е известно, че този резултат ще се пази дълго време, а EAX се използва като scratch променлива.
Такива фокуси в никой език от високо ниво не са възможни (или поне аз не знам такива езици).
В асемблера няма понятие като ABI – можеш да си правиш каквото си искаш. Но предаването на параметри в регистрите, когато става въпрос за библиотечни функции не е добра идея. За някакви малки спомагателни функции, да, без проблем.
Обаче нали кода който пишеш трябва да е в PE/ELF формат (за windows/linux) и трябва да правиш syscalls за да заделиш и получиш от OS пойнтер към динамичната памет/heap, т.е. поне при тези OSи си имаш процес и виртуална памет, нямаш достъп до физическата. И като правиш тези syscalls трябва да спазваш ABI конвенциите.
Естествено. Но това е ABI-то на операционната система. При Уиндоус е едно, при Линукс съвсем друго.
А вътре в приложението, плюс библиотеките, които сам си пишеш можеш да си използваш каквото си искаш.
Разбира се, човек си изработва някакви правила и "стандарти", но специално за асемблера те са много плаващи и зависят повече от контекста в който се използва някаква конкретна функция.
Аз например масово използвам CF флага за връщане на булеви резултати, просто защото е супер удобно. Имам функции обаче, които връщат булев резултат в ZF.
Имам някои функции, които връщат по няколко резултата в няколко регистъра.
Ако в 90% функциите връщат резултат в EAX, то в някои случаи е по-удобно да се връща резултата примерно в EBX или в EDX, защото предварително е известно, че този резултат ще се пази дълго време, а EAX се използва като scratch променлива.
Такива фокуси в никой език от високо ниво не са възможни (или поне аз не знам такива езици).
Кое е фокус, да върнеш няколко резултата с една функция ли?
Дай нещо конкретно, ето казваш например да видим има ли функция дето връща 4 32-битови стойности
waldorf
Създадено на 10.03.2023, видяно: 406 пъти. #86939
gcc и clang май позволяваха да специфицираш в кои регистри са параметри и връщания резултат ... или пък беше Watcom C/C++ ... помня ли вече.
Ползването на регистри за параметри има един голям недостатък - компилатора непрекъснато преподрежда променливите в регистрите за да угоди на функцията която извиква т.е. ако ги подредиш по "правилен" начин параметрите на функциите може да спестиш някоя друга инструкция избягвайки това преподреждане.
Когато се предават през стека който пък от своя страна е добре кеширан колкото и да е парадоксално на практика може да се окаже по бързо отколкото спрямо когато са в регистри. Но и тук си има специфики защото ако стане прекъсване в този момент процесора трябва да запише всичко кеширано в бавната външна памет за да гарантира, че някоя друга нишка която също ползва тази памет ще види верните данни а не тези отпреди малко дори и когато въобще няма такава нишка.
Едно време с делфинариума си беше купон да блъскаш асемблер - https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Assembly_Procedures_and_Functions
Но такива бяха времената, романтични, аз съм работил и във фирма (пак с делфи) където производителността беше топ изискване - ставаше дума за парсване на html - и стандартните библиотеки за низове се считаха и с право за относително бавни. ОБАЧЕ и тук го имаше тоя момент че обекта на парсването често се променяше, че имаше разни интеграции и модули, Джони може да се чуства горд, и там ползвахме sqlite т.е. нормална производствена обстановка. Така че разумния компромис си беше обработка с паскал, а не с асемблерски код