Кажется, что сейчас работает:
jQuery(document).ajaxError(function(event, request, settings){
alert("Error");
});
Мало того, что процессоры x86 способны считывать и записывать один байт, на это способны все современные процессоры общего назначения. Что еще более важно, большинство современных процессоров (включая x86, ARM, MIPS, PowerPC и SPARC) способны атомарно считывать и записывать отдельные байты.
Я не уверен, о чем говорил Страуструп. Раньше было несколько машин с адресацией слов, которые не были способны к 8-битной адресации байтов, например Cray, и, как сказал Питер Кордес, ранние процессоры Alpha не поддерживали загрузку и хранение байтов, но сегодня единственные процессоры, неспособные к байту, сегодня. нагрузки и хранилища - это определенные DSP, используемые в нишевых приложениях. Даже если предположить, что он имеет в виду, что большинство современных процессоров не имеют атомарной байтовой нагрузки и сохраняют данные, это не относится к большинству процессоров.
Тем не менее, простые атомарные загрузки и хранилища не очень полезны в многопоточном программировании. Вам также обычно нужны гарантии упорядочения и способ сделать операции чтения-изменения-записи атомарными. Другое соображение заключается в том, что, хотя процессор a может иметь инструкции загрузки и хранения байтов, компилятору не требуется их использовать. Например, компилятор все еще может генерировать код, описанный Страуструпом, загружая как b
, так и c
, используя инструкцию загрузки одного слова в качестве оптимизации.
Поэтому, хотя вам нужна четко определенная модель памяти, если только так компилятор вынужден генерировать код, который вы ожидаете, проблема не в том, что современные процессоры не способны загружать или хранить что-то меньшее, чем слово.
Это правильно. Процессор x86_64, как и оригинальный процессор x86, не может читать или записывать что-либо меньшее, чем (в данном случае 64-битное) слово из rsp. в память. И он обычно не читает и не записывает меньше, чем целая строка кэша, хотя существуют способы обхода кэша, особенно при записи (см. Ниже).
В этом контексте , однако, Страуструп ссылается на потенциальные гонки данных (отсутствие атомарности на наблюдаемом уровне). Эта проблема правильности не имеет отношения к x86_64 из-за протокола когерентности кэша, о котором вы упомянули. Другими словами, да, процессор ограничен передачей целых слов, , но это прозрачно обрабатывается, и вам, как программисту, вообще не нужно беспокоиться об этом. Фактически, язык C ++, начиная с C ++ 11, гарантирует , что параллельные операции в разных местах памяти имеют четко определенное поведение, то есть то, которое вы ожидаете. Даже если бы аппаратное обеспечение не гарантировало этого, реализация должна была бы найти способ, генерируя, возможно, более сложный код.
Тем не менее, все еще может быть хорошей идеей сохранить тот факт, что целые слова или даже строки кэша всегда находятся на уровне машины в задней части вашей головы, по двум причинам.
volatile
жизненно важно для предотвращения такой неподходящей оптимизации. Вот - несколько надуманный пример очень плохой структуры данных. Предположим, у вас есть 16 потоков, разбирающих текст из файла. Каждый поток имеет id
от 0 до 15.
// shared state
char c[16];
FILE *file[16];
void threadFunc(int id)
{
while ((c[id] = getc(file[id])) != EOF)
{
// ...
}
}
Это безопасно, потому что каждый поток работает в разных местах памяти. Однако эти области памяти обычно находятся на одной и той же строке кэша или, самое большее, разделяются на две строки кэша. Протокол когерентности кэша затем используется для правильной синхронизации доступа к c[id]
. И в этом заключается проблема, потому что это заставляет каждый другой поток ждать, пока строка кэша не станет исключительно доступной, прежде чем делать что-либо с c[id]
, если только он не работает на ядре, которое «владеет» строкой кэша , Предполагая несколько, например, 16, ядра, когерентность кэша, как правило, будут постоянно переносить строку кэша от одного ядра к другому. По понятным причинам этот эффект известен как «пинг-понг кеша». Это создает ужасное узкое место в производительности. Это результат очень плохого случая ложного совместного использования , то есть потоков, совместно использующих физическую строку кэша без фактического доступа к тем же логическим ячейкам памяти.
В отличие от этого, особенно если предпринять дополнительный шаг, чтобы убедиться, что массив file
находится в собственной строке кэша, его использование будет совершенно безвредным (на x86_64) с точки зрения производительности, поскольку указатели считываются только от, в большинстве случаев. В этом случае несколько ядер могут «совместно использовать» строку кэша только для чтения. Только когда любое ядро пытается выполнить запись в строку кэша, оно должно сообщить другим ядрам, что оно собирается «захватить» строку кэша для монопольного доступа.
(Это значительно упрощается, поскольку существуют разные уровни кэшей ЦП, и несколько ядер могут использовать один и тот же кэш L2 или L3, но это должно дать вам общее представление о проблеме.)
Не уверен, что Страуструп имел в виду под «СЛОВОМ». Может быть, это минимальный объем памяти машины?
В любом случае не все машины были созданы с разрешением 8 бит (BYTE). На самом деле, я рекомендую эту замечательную статью Эрика С. Рэймонда, описывающую историю компьютеров: http://www.catb.org/esr/faqs/things-every-hacker-once-knew/
"... Раньше также было общеизвестно, что 36-битные архитектуры объясняют некоторые прискорбные особенности языка Си. Оригинальная машина Unix, PDP-7, содержала 18-битные слова, соответствующие полуслов на больших 36-битных компьютерах. Более естественно они представлены в виде шести восьмеричных (3-битных) цифр. "
Автор, похоже, обеспокоен тем, что поток 1 и поток 2 попадают в ситуацию, когда чтение-изменение-запись (не в программном обеспечении, программное обеспечение выполняет две отдельные инструкции размером в байт, где-то внизу должна выполняться логика строки). чтение-изменение-запись) вместо идеальной операции чтения-изменения-записи-чтения-изменения-записи становится записью-чтением-изменением-записью-записью или каким-либо другим моментом, так что обе считывают предварительно измененную версию и побеждает последняя из записанных версий. чтение чтение изменение изменение запись запись или чтение изменение чтение изменение запись запись или чтение изменение чтение запись изменение запись.
Задача состоит в том, чтобы начать с 0x1122, и один поток хочет сделать его 0x33XX, а другой хочет сделать его 0xXX44, но, например, с чтением-изменением-изменением-записью-записью вы в конечном итоге получите 0x1144 или 0x3322, но не 0x3344
В здравом (системном / логическом) дизайне такой проблемы просто нет, конечно, не для процессора общего назначения, подобного этому, я работал над проектами с такими проблемами синхронизации, но это не то, о чем мы здесь говорим, совершенно разные конструкции систем для разных целей. Чтение-изменение-запись не распространяется на достаточно большое расстояние в нормальном дизайне, а x86 - это нормальные проекты.
Чтение-изменение-запись должно происходить очень близко к первой задействованной SRAM (в идеале L1 при обычном запуске x86 с операционной системой, способной запускать многопоточные программы, скомпилированные в C ++) и происходить в течение нескольких тактовые циклы, поскольку оперативная память находится на скорости шины в идеале. И, как указал Питер, это считается целой строкой кеша, которая испытывает это внутри кеша, а не чтение-модификация-запись между ядром процессора и кешем.
Понятие «в одно и то же время» даже с многоядерными системами не обязательно в одно и то же время, в конце концов вы получаете сериализацию, потому что производительность не основана на их параллельности от начала до конца, она основана на держать автобусы загруженными.
Цитата говорит, что переменные размещены в памяти для одного и того же слова, так что это одна и та же программа. Две отдельные программы не собираются использовать такое же адресное пространство. поэтому
Вы можете попробовать это, сделать многопоточную программу, которая пишет по адресу 0xnnn00000, а другая записывает по адресу 0xnnn00001, каждая выполняет запись, а затем выполняет чтение или, что лучше, несколько записей одного и того же значения, чем одна. прочитайте, проверьте, что чтение было записанным байтом, а затем повторяется с другим значением. Позвольте этому бежать некоторое время, часы / дни / недели / месяцы. Посмотрите, отключаете ли вы систему ... используйте сборку для фактических инструкций записи, чтобы убедиться, что она выполняет то, что вы просили (не C ++ или любой компилятор, который делает или утверждает, что не поместит эти элементы в одно и то же слово). Можно добавить задержки, чтобы увеличить количество кэш-памяти, но это уменьшает ваши шансы «одновременных» коллизий.
Ваш пример, если вы гарантируете, что вы не находитесь по двум сторонам границы (кеш или другое), например 0xNNNNFFFFF и 0xNNNN00000, изолируйте две байтовые записи по адресам, таким как 0xNNNN00000 и 0xNNNN00001, инструкции возвращаются назад и посмотрите, получите ли вы чтение, чтение, изменение, запись и запись. Оберните тест вокруг этого, что два значения различны в каждом цикле, вы читаете слово в целом с любой задержкой позже, когда захотите, и проверяете два значения. Повторите для дней / недель / месяцев / лет, чтобы увидеть, если это не удается. Ознакомьтесь с функциями выполнения вашего процессора и микрокода, чтобы увидеть, что он делает с этой последовательностью инструкций, и при необходимости создайте другую последовательность инструкций, которая пытается инициировать транзакции в течение нескольких или около того тактовых циклов на дальней стороне ядра процессора.
РЕДАКТИРОВАТЬ
проблема с кавычками заключается в том, что это все о языке и использовании. «Как и большинство современных аппаратных средств» ставит всю тему / текст в очень обидчивое положение, это слишком расплывчато, одна сторона может поспорить, все, что мне нужно сделать, - это найти один случай, который является правдой, чтобы сделать все остальное правдой, Точно так же одна сторона может поспорить, если я найду один случай, а все остальные не соответствуют действительности. Используя это слово, вроде путаницы с этим, можно выйти из тюрьмы бесплатно.
Реальность такова, что значительный процент наших данных хранится в памяти DRAM в 8-битной памяти, просто мы не обращаемся к ним как к 8-битной, обычно мы получаем доступ к 8 из них за раз, 64-битным широкий. Через несколько недель / месяцев / лет / десятилетий это утверждение будет неверным.
Более крупная цитата гласит «одновременно», а затем гласит: «прочитайте ... сначала, напишите ... наконец, хорошо, сначала и последнее, и в то же время не имеет смысла вместе, параллельно или последовательно? Контекст в целом связан с описанными выше вариантами чтения-чтения-изменения-записи-записи, когда у вас последняя запись и в зависимости от того, когда это чтение определяет, произошли обе модификации или нет. Не в то же самое время, когда «как большинство современных аппаратных средств» не имеет смысла, вещи, которые начинаются фактически параллельно в отдельных ядрах / модулях, в конечном счете, сериализуются, если они нацелены на один и тот же триггер / транзистор в памяти, один в конечном итоге должен ждать другого, чтобы идти первым. Основываясь на физике, я не вижу в этом неправильности в ближайшие недели / месяцы / годы.