精华内容
下载资源
问答
  • 1.2 并行编程语言 在并行计算发展史上出现过多种并行编程语言,至今仍在使用的只剩几种,它们各有特色。 (1)Pthreads 20世纪70年代,贝尔实验室发明了UNIX,并于20世纪80年代向美国各大高校...

    本节书摘来自华章出版社《OpenACC并行编程实战》一 书中的第1章,第1.2节,作者何沧平,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

    1.2 并行编程语言

    在并行计算发展史上出现过多种并行编程语言,至今仍在使用的只剩几种,
    它们各有特色。

    (1)Pthreads

    20世纪70年代,贝尔实验室发明了UNIX,并于20世纪80年代向美国各大高校分发V7版的源码以做研究。加利福尼亚大学伯克利分校在V7的基础上开发了BSD UNIX。后来很多商业厂家意识到UNIX的价值也纷纷以贝尔实验室的System V或BSD为基础来开发自己的UNIX,较著名的有Sun OS、AIX、VMS。随着操作系统的增多,应用程序的适配性工作越来越繁重。为了提高UNIX环境下应用程序的可迁移性,电气和电子工程师协会(Institute of Electrical and Electronics Engineers,IEEE)设计了POSIX标准。然而,POSIX并不局限于UNIX,许多其他的操作系统也支持POSIX标准。POSIX.1已经被国际标准化组织所接受,POSIX已发展成为一个非常庞大的标准族,一直处在发展之中。
    POSIX线程(POSIX threads,Pthreads),是线程的POSIX标准。该标准定义了创建和操纵线程的一整套接口。在类UNIX操作系统(UNIX、Linux、Mac OS X等)中,都使用Pthreads作为操作系统的线程。Pthreads用来开发与操作系统紧密相关的应用程序,管理粒度很细,例如线程的创建与销毁、线程锁、线程属性、线程优先级、线程间通信等琐碎操作均需要程序员安排。对科学与工程类计算程序来说,程序员的精力应集中在业务模型和代码算法上,不应浪费在底层代码细节上。
    虽然Pthreads不适合编写高性能计算程序,但它多线程并发的设计理念启发了其他并行语言。

    (2)OpenMP

    OpenMP是由一些大型IT厂商和一些学术机构组成的非盈利组织,官网是www.openmp.org。永久成员包括AMD、CAPS-Entreprise、Convey Computer、Cray、Fujitsu、HP、IBM、Intel、NEC、NVIDIA、Oracle Corporation、Red Hat、ST Microelectronics、Texas Instruments;正式成员是对OpenMP标准感兴趣,但不生产销售相关产品的组织,例如ANL、ASC/LLNL、BSC、cOMPunity、EPCC、LANL、NASA、ORNL、RWTH Aachen University、SNL-Sandia National Lab、Texas Advanced Computing Center、University of Houston。
    计算热点都是在循环上,OpenMP的并行化思路是将循环的迭代步分摊到多个线程上,每个线程只承担一部分计算任务,循环运行的墙上时间(从开始到结束的自然流逝时间)自然也就减少了。分割方法是在循环上面添加一些预处理标记(图1.18),编译器识别到这些标记以后,将关联的循环翻译成并行代码,然后再与剩余的串行代码合并起来编译、链接成可执行程序。

    <img src="https://yqfile.alicdn.com/f8fe331811903d5ff604245a30fe84de1de14712.png" width="" height="">


    使用OpenMP编译并行程序时,程序员需要先保证串行代码正确,找出热点循环,然后在循环上添加OpenMP预处理标记。打开编译器的openmp选项可以编译成并行版本,否则编译器将忽略预处理器标记,仍然编译成串行版本。既不破坏原有代码,开发速度又快,省时省力。

    (3)CUDA

    CUDA(Compute Unified Device Architecture)是英伟达公司设计的GPU并行编程语言,一经推出就引发了GPU通用计算研究热潮。CUDA是闭源的,只能运行在英伟达的产品上。CUDA C/C++是对C/C++语言的扩展,添加了一些数据类型、库函数,并定义一种新的函数调用形式。CUDA起初支持C和少量C++特性,后来逐渐提高对C++的支持度。从CUDA 3.0开始与PGI合作支持Fortran。CUDA语言还可以细分为CUDA C/C++、CUDA Fortran,本书成稿时的CUDA C/C++最新版本是7.5,8.0版本即将发布。CUDA Fortran没有版本号,只是随着PGI编译器的升级而增加新特性。1.3节会介绍CUDA C/C++的编程模型和一些示例代码,此处不展开介绍。

    (4)OpenCL

    OpenCL(Open Computing Language,开放计算语言)是一个面向异构系统并行编程的免费标准,支持多种多样的设备,包括但不限于CPU、GPU、数字信号处理器(DSP)。OpenCL的优势是一套代码多处运行,只要为新的设备重新编译代码就可以运行,移植方便。
    OpenCL由苹果公司首先提出,随后Khronos Group成立相关工作组,以苹果草案为基础,联合业界各大企业共同完成了标准制定工作,工作组的成员来自各行各业,且都是各自领域的领导者。

    (5)OpenACC

    这里不多说,后面会全面、详细讲解。

    展开全文
  • Chapel是一种新兴的并行编程语言,其设计和开发由Cray Inc.与学术界,计算实验室和行业合作进行。 Chapel的目标是提高并行编程器的生产率,从最大规模的超级计算机到商品集群以及多核台式机和笔记本电脑。
  • PARUS是一种数据流并行编程语言,允许为集群和MPP多处理器构建并行程序。 数据流图会自动转换为C ++ / MPI源,并与libparus运行时库链接。 此外,还有一些可用的工具可用于对群集互连进行基准测试并对其进行可视化。
  • PythonPython开发Python语言Python3 系列之 并行编程 进程和线程进程是程序运行的实例。一个进程里面可以包含多个线程,因此同一进程下的多个线程之间可以共享线程内的所有资源,它是操作系统动态运行的基本单元;每...

    Python

    Python开发

    Python语言

    Python3 系列之 并行编程

    进程和线程

    进程是程序运行的实例。一个进程里面可以包含多个线程,因此同一进程下的多个线程之间可以共享线程内的所有资源,它是操作系统动态运行的基本单元;每一个线程是进程下的一个实例,可以动态调度和独立运行,由于线程和进程有很多类似的特点,因此,线程又被称为轻量级的进程。线程的运行在进程之下,进程的存在依赖于线程;

    开胃菜

    基于 Python3 创建一个简单的进程示例

    from threading import Thread

    from time import sleep

    class CookBook(Thread):

    def __init__(self):

    Thread.__init__(self)

    self.message = "Hello Parallel Python CookBook!!n"

    def print_message(self):

    print(self.message)

    def run(self):

    print("Thread Startingn")

    x = 0

    while x < 10:

    self.print_message()

    sleep(2)

    x += 1

    print("Thread Ended!n")

    print("Process Started")

    hello_python = CookBook()

    hello_python.start()

    print("Process Ended")

    需要注意的是,永远不要让线程在后台默默执行,当其执行完毕后要及时释放资源。

    基于线程的并行

    多线程编程一般使用共享内存空间进行线程间的通信,这就使管理内存空间成为多线程编程的关键。Python 通过标准库 threading 模块来管理线程,具有以下的组件:

    线程对象

    Lock 对象

    RLock 对象

    信号对象

    条件对象

    事件对象

    定义一个线程

    基本语法

    示例代码如下所示

    import threading

    def function(i):

    print("function called by thread: {0}".format(i))

    return

    threads = []

    for i in range(5):

    t = threading.Thread(target=function, args=(i,))

    threads.append(t)

    t.start()

    lambda t, threads: t.join()

    需要注意的是,线程创建后并不会自动运行,需要主动调用 start() 方法来启动线程,join() 会让调用它的线程被阻塞直到执行结束。(PS:可通过调用 t.setDaemon(True) 使其为后台线程避免主线程被阻塞)

    线程定位

    示例代码如下所示

    import threading

    import time

    def first_function():

    print("{0} is starting".format(threading.currentThread().getName()))

    time.sleep(2)

    print("{0} is Exiting".format(threading.currentThread().getName()))

    def second_function():

    print("{0} is starting".format(threading.currentThread().getName()))

    time.sleep(2)

    print("{0} is Exiting".format(threading.currentThread().getName()))

    def third_function():

    print("{0} is starting".format(threading.currentThread().getName()))

    time.sleep(2)

    print("{0} is Exiting".format(threading.currentThread().getName()))

    if __name__ == "__main__":

    t1 = threading.Thread(target=first_function,name="first")

    t2 = threading.Thread(target=second_function,name="second")

    t3 = threading.Thread(target=third_function,name="third")

    t1.start()

    t2.start()

    t3.start()

    t1.join()

    t2.join()

    t3.join()

    通过设置 threading.Thread() 函数的 name 参数来设置线程名称,通过 threading.currentThread().getName() 来获取当前线程名称;线程的默认名称会以 Thread-{i} 格式来定义

    自定义一个线程对象

    示例代码如下所示

    import threading

    import time

    exitFlag = 0

    class myThread(threading.Thread):

    def __init__(self, threadID, name, counter):

    threading.Thread.__init__(self)

    self.threadID = threadID

    self.name = name

    self.counter = counter

    def run(self):

    print("Starting:{0}".format(self.name))

    print_time(self.name, self.counter, 5)

    print("Exiting:{0}".format(self.name))

    def print_time(threadName, delay, counter):

    while counter:

    if exitFlag:

    thread.exit()

    time.sleep(delay)

    print("{0} {1}".format(threadName, time.ctime(time.time())))

    counter -= 1

    t1 = myThread(1, "Thread-1", 1)

    t2 = myThread(2, "Thread-2", 1)

    t1.start()

    t2.start()

    t1.join()

    t2.join()

    print("Exiting Main Thread.")

    如果想自定义一个线程对象,首先就是要定义一个继承 threading.Thread 类的子类,实现构造函数, 并重写 run() 方法即可。

    线程同步

    Lock

    示例代码如下所示

    import threading

    shared_resource_with_lock = 0

    shared_resource_with_no_lock = 0

    COUNT = 100000

    shared_resource_lock = threading.Lock()

    def increment_with_lock():

    global shared_resource_with_lock

    for i in range(COUNT):

    shared_resource_lock.acquire()

    shared_resource_with_lock += 1

    shared_resource_lock.release()

    def decrement_with_lock():

    global shared_resource_with_lock

    for i in range(COUNT):

    shared_resource_lock.acquire()

    shared_resource_with_lock -= 1

    shared_resource_lock.release()

    def increment_without_lock():

    global shared_resource_with_no_lock

    for i in range(COUNT):

    shared_resource_with_no_lock += 1

    def decrement_wthout_lock():

    global shared_resource_with_no_lock

    for i in range(COUNT):

    shared_resource_with_no_lock -= 1

    if __name__ == "__main__":

    t1 = threading.Thread(target=increment_with_lock)

    t2 = threading.Thread(target=decrement_with_lock)

    t3 = threading.Thread(target=increment_without_lock)

    t4 = threading.Thread(target=decrement_wthout_lock)

    t1.start()

    t2.start()

    t3.start()

    t4.start()

    t1.join()

    t2.join()

    t3.join()

    t4.join()

    print("the value of shared variable with lock management is :{0}".format(

    shared_resource_with_lock))

    print("the value of shared variable with race condition is :{0}".format(

    shared_resource_with_no_lock))

    通过 threading.Lock() 方法我们可以拿到线程锁,一般有两种操作方式:acquire() 和 release() 在两者之间是加锁状态,如果释放失败的话会显示 RuntimError() 的异常。

    RLock

    RLock 也叫递归锁,和 Lock 的区别在于:谁拿到谁释放,是通过 threading.RLock() 来拿到的;

    示例代码如下所示

    import threading

    import time

    class Box(object):

    lock = threading.RLock()

    def __init__(self):

    self.total_items = 0

    def execute(self, n):

    Box.lock.acquire()

    self.total_items += n

    Box.lock.release()

    def add(self):

    Box.lock.acquire()

    self.execute(1)

    Box.lock.release()

    def remove(self):

    Box.lock.acquire()

    self.execute(-1)

    Box.lock.release()

    def adder(box, items):

    while items > 0:

    print("adding 1 item in the box")

    box.add()

    time.sleep(1)

    items -= 1

    def remover(box, items):

    while items > 0:

    print("removing 1 item in the box")

    box.remove()

    time.sleep(1)

    items -= 1

    if __name__ == "__main__":

    items = 5

    print("putting {0} items in the box".format(items))

    box = Box()

    t1 = threading.Thread(target=adder, args=(box, items))

    t2 = threading.Thread(target=remover, args=(box, items))

    t1.start()

    t2.start()

    t1.join()

    t2.join()

    print("{0} items still remain in the box".format(box.total_items))

    信号量

    示例代码如下所示

    import threading

    import time

    import random

    semaphore = threading.Semaphore(0)

    def consumer():

    print("Consumer is waiting.")

    semaphore.acquire()

    print("Consumer notify:consumed item numbers {0}".format(item))

    def producer():

    global item

    time.sleep(10)

    item = random.randint(0, 10000)

    print("producer notify:produced item number {0}".format(item))

    semaphore.release()

    if __name__ == "__main__":

    for i in range(0, 5):

    t1 = threading.Thread(target=producer)

    t2 = threading.Thread(target=consumer)

    t1.start()

    t2.start()

    t1.join()

    t2.join()

    print("program terminated.")

    信号量初始化为 0 ,然后在两个并行线程中,通过调用 semaphore.acquire() 函数会阻塞消费者线程,直到 semaphore.release() 在生产者中被调用,这里模拟了生产者-消费者 模式来进行了测试;如果信号量的计数器到了0,就会阻塞 acquire() 方法,直到得到另一个线程的通知。如果信号量的计数器大于0,就会对这个值-1然后分配资源。

    使用条件进行线程同步

    解释条件机制最好的例子还是生产者-消费者问题。在本例中,只要缓存不满,生产者一直向缓存生产;只要缓存不空,消费者一直从缓存取出(之后销毁)。当缓冲队列不为空的时候,生产者将通知消费者;当缓冲队列不满的时候,消费者将通知生产者。

    示例代码如下所示

    from threading import Thread, Condition

    import time

    items = []

    condition = Condition()

    class consumer(Thread):

    def __init__(self):

    Thread.__init__(self)

    def consume(self):

    global condition

    global items

    condition.acquire()

    if len(items) == 0:

    condition.wait()

    print("Consumer notify:no item to consum")

    items.pop()

    print("Consumer notify: consumed 1 item")

    print("Consumer notify: item to consume are:{0}".format(len(items)))

    condition.notify()

    condition.release()

    def run(self):

    for i in range(0, 20):

    time.sleep(2)

    self.consume()

    class producer(Thread):

    def __init__(self):

    Thread.__init__(self)

    def produce(self):

    global condition

    global items

    condition.acquire()

    if len(items) == 10:

    condition.wait()

    print("Producer notify:items producted are:{0}".format(len(items)))

    print("Producer notify:stop the production!!")

    items.append(1)

    print("Producer notify:total items producted:{0}".format(len(items)))

    condition.notify()

    condition.release()

    def run(self):

    for i in range(0, 20):

    time.sleep(1)

    self.produce()

    if __name__ == "__main__":

    producer = producer()

    consumer = consumer()

    producer.start()

    consumer.start()

    producer.join()

    consumer.join()

    通过 condition.acquire() 来获取锁对象,condition.wait() 会使当前线程进入阻塞状态,直到收到 condition.notify() 信号,同时,调用信号的通知的对象也要及时调用 condition.release() 来释放资源;

    使用事件进行线程同步

    事件是线程之间用于通信的对。有的线程等待信号,有的线程发出信号。

    示例代码如下所示

    import time

    from threading import Thread, Event

    import random

    items = []

    event = Event()

    class consumer(Thread):

    def __init__(self, items, event):

    Thread.__init__(self)

    self.items = items

    self.event = event

    def run(self):

    while True:

    time.sleep(2)

    self.event.wait()

    item = self.items.pop()

    print('Consumer notify:{0} popped from list by {1}'.format(

    item, self.name))

    class producer(Thread):

    def __init__(self, integers, event):

    Thread.__init__(self)

    self.items = items

    self.event = event

    def run(self):

    global item

    for i in range(100):

    time.sleep(2)

    item = random.randint(0, 256)

    self.items.append(item)

    print('Producer notify: item N° %d appended to list by %s' %

    (item, self.name))

    print('Producer notify: event set by %s' % self.name)

    self.event.set()

    print('Produce notify: event cleared by %s ' % self.name)

    self.event.clear()

    if __name__ == "__main__":

    t1 = producer(items, event)

    t2 = consumer(items, event)

    t1.start()

    t2.start()

    t1.join()

    t2.join()

    使用 with 语法简化代码

    import threading

    import logging

    logging.basicConfig(level=logging.DEBUG,

    format='(%(threadName)-10s) %(message)s')

    def threading_with(statement):

    with statement:

    logging.debug("%s acquired via with" % statement)

    def Threading_not_with(statement):

    statement.acquire()

    try:

    logging.debug("%s acquired directly " % statement)

    finally:

    statement.release()

    if __name__ == "__main__":

    lock = threading.Lock()

    rlock = threading.RLock()

    condition = threading.Condition()

    mutex = threading.Semaphore(1)

    threading_synchronization_list = [lock, rlock, condition, mutex]

    for statement in threading_synchronization_list:

    t1 = threading.Thread(target=threading_with, args=(statement,))

    t2 = threading.Thread(target=Threading_not_with, args=(statement,))

    t1.start()

    t2.start()

    t1.join()

    t2.join()

    使用 queue 进行线程通信

    Queue 常用的方法有以下四个:

    put():往 queue 中添加一个元素

    get():从 queue 中删除一个元素,并返回该元素

    task_done():每次元素被处理的时候都需要调用这个方法

    join():所有元素都被处理之前一直阻塞from threading import Thread, Event

    from queue import Queue

    import time

    import random

    class producer(Thread):

    def __init__(self, queue):

    Thread.__init__(self)

    self.queue = queue

    def run(self):

    for i in range(10):

    item = random.randint(0, 256)

    self.queue.put(item)

    print("Producer notify: item item N° %d appended to queue by %s" %

    (item, self.name))

    time.sleep(1)

    class consumer(Thread):

    def __init__(self, queue):

    Thread.__init__(self)

    self.queue = queue

    def run(self):

    while True:

    item = self.queue.get()

    print('Consumer notify : %d popped from queue by %s' %

    (item, self.name))

    self.queue.task_done()

    if __name__ == "__main__":

    queue = Queue()

    t1 = producer(queue)

    t2 = consumer(queue)

    t3 = consumer(queue)

    t4 = consumer(queue)

    t1.start()

    t2.start()

    t3.start()

    t4.start()

    t1.join()

    t2.join()

    t3.join()

    t4.join()

    基于进程的并行

    multiprocessing 是 Python 标准库中的模块,实现了共享内存机制。

    异步编程

    使用 concurrent.futures 模块

    该模块具有线程池和进程池,管理并行编程任务、处理非确定性的执行流程、进程/线程同步等功能;此模块由以下部分组成

    concurrent.futures.Executor: 这是一个虚拟基类,提供了异步执行的方法。

    submit(function, argument): 调度函数(可调用的对象)的执行,将 argument 作为参数传入。

    map(function, argument): 将 argument 作为参数执行函数,以 异步 的方式。

    shutdown(Wait=True): 发出让执行者释放所有资源的信号。

    concurrent.futures.Future: 其中包括函数的异步执行。Future对象是submit任务(即带有参数的functions)到executor的实例。

    示例代码如下所示

    import concurrent.futures

    import time

    number_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    def evaluate_item(x):

    result_item = count(x)

    return result_item

    def count(number):

    for i in range(0, 1000000):

    i = i + 1

    return i * number

    if __name__ == "__main__":

    # 顺序执行

    start_time = time.time()

    for item in number_list:

    print(evaluate_item(item))

    print("Sequential execution in " + str(time.time() - start_time), "seconds")

    # 线程池执行

    start_time_1 = time.time()

    with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:

    futures = [executor.submit(evaluate_item, item)

    for item in number_list]

    for future in concurrent.futures.as_completed(futures):

    print(future.result())

    print("Thread pool execution in " +

    str(time.time() - start_time_1), "seconds")

    # 线程池执行

    start_time_2 = time.time()

    with concurrent.futures.ProcessPoolExecutor(max_workers=5) as executor:

    futures = [executor.submit(evaluate_item, item)

    for item in number_list]

    for future in concurrent.futures.as_completed(futures):

    print(future.result())

    print("Process pool execution in " +

    str(time.time() - start_time_2), "seconds")

    使用 Asyncio 管理事件循环

    Python 的 Asyncio 模块提供了管理事件、协程、任务和线程的方法,以及编写并发代码的原语。此模块的主要组件和概念包括:

    事件循环: 在Asyncio模块中,每一个进程都有一个事件循环。

    协程: 这是子程序的泛化概念。协程可以在执行期间暂停,这样就可以等待外部的处理(例如IO)完成之后,从之前暂停的地方恢复执行。

    Futures: 定义了 Future 对象,和 concurrent.futures 模块一样,表示尚未完成的计算。

    Tasks: 这是Asyncio的子类,用于封装和管理并行模式下的协程。

    Asyncio 提供了以下方法来管理事件循环:

    loop = get_event_loop(): 得到当前上下文的事件循环。

    loop.call_later(time_delay, callback, argument): 延后 time_delay 秒再执行 callback 方法。

    loop.call_soon(callback, argument): 尽可能快调用 callback, call_soon() 函数结束,主线程回到事件循环之后就会马上调用 callback 。

    loop.time(): 以float类型返回当前时间循环的内部时间。

    asyncio.set_event_loop(): 为当前上下文设置事件循环。

    asyncio.new_event_loop(): 根据此策略创建一个新的时间循环并返回。

    loop.run_forever(): 在调用 stop() 之前将一直运行。

    示例代码如下所示

    import asyncio

    import datetime

    import time

    def fuction_1(end_time, loop):

    print("function_1 called")

    if(loop.time() + 1.0) < end_time:

    loop.call_later(1, fuction_2, end_time, loop)

    else:

    loop.stop()

    def fuction_2(end_time, loop):

    print("function_2 called")

    if(loop.time() + 1.0) < end_time:

    loop.call_later(1, function_3, end_time, loop)

    else:

    loop.stop()

    def function_3(end_time, loop):

    print("function_3 called")

    if(loop.time() + 1.0) < end_time:

    loop.call_later(1, fuction_1, end_time, loop)

    else:

    loop.stop()

    def function_4(end_time, loop):

    print("function_4 called")

    if(loop.time() + 1.0) < end_time:

    loop.call_later(1, function_4, end_time, loop)

    else:

    loop.stop()

    loop = asyncio.get_event_loop()

    end_loop = loop.time() + 9.0

    loop.call_soon(fuction_1, end_loop, loop)

    loop.run_forever()

    loop.close()

    使用 Asyncio 管理协程

    示例代码如下所示

    import asyncio

    import time

    from random import randint

    @asyncio.coroutine

    def StartState():

    print("Start State called n")

    input_val = randint(0, 1)

    time.sleep(1)

    if input_val == 0:

    result = yield from State2(input_val)

    else:

    result = yield from State1(input_val)

    print("Resume of the Transition:nStart State calling" + result)

    @asyncio.coroutine

    def State1(transition_value):

    outputVal = str("State 1 with transition value=%s n" % (transition_value))

    input_val = randint(0, 1)

    time.sleep(1)

    print("...Evaluating...")

    if input_val == 0:

    result = yield from State3(input_val)

    else:

    result = yield from State2(input_val)

    @asyncio.coroutine

    def State2(transition_value):

    outputVal = str("State 2 with transition value= %s n" %

    (transition_value))

    input_Val = randint(0, 1)

    time.sleep(1)

    print("...Evaluating...")

    if (input_Val == 0):

    result = yield from State1(input_Val)

    else:

    result = yield from State3(input_Val)

    result = "State 2 calling " + result

    return outputVal + str(result)

    @asyncio.coroutine

    def State3(transition_value):

    outputVal = str("State 3 with transition value = %s n" %

    (transition_value))

    input_val = randint(0, 1)

    time.sleep(1)

    print("...Evaluating...")

    if(input_val == 0):

    result = yield from State1(input_val)

    else:

    result = yield from State2(input_val)

    result = "State 3 calling " + result

    return outputVal + str(result)

    @asyncio.coroutine

    def EndState(transition_value):

    outputVal = str("End State With transition value = %s n" %

    (transition_value))

    print("...Stop Computation...")

    return outputVal

    if __name__ == "__main__":

    print("Finites State Machine simulation with Asyncio Coroutine")

    loop = asyncio.get_event_loop()

    loop.run_until_complete(StartState())

    使用 Asyncio 控制任务

    示例代码如下所示

    import asyncio

    @asyncio.coroutine

    def factorial(number):

    f = 1

    for i in range(2, number + 1):

    print("Asyncio.Task:Compute factorial(%s)" % (i))

    yield from asyncio.sleep(1)

    f *= i

    print("Asyncio.Task - factorial(%s) = %s" % (number, f))

    @asyncio.coroutine

    def fibonacci(number):

    a, b = 0, 1

    for i in range(number):

    print("Asyncio.Task:Complete fibonacci (%s)" % (i))

    yield from asyncio.sleep(1)

    a, b = b, a+b

    print("Asyncio.Task - fibonaci (%s)= %s" % (number, a))

    @asyncio.coroutine

    def binomialCoeff(n, k):

    result = 1

    for i in range(1, k+1):

    result = result * (n-i+1) / i

    print("Asyncio.Task:Compute binomialCoeff (%s)" % (i))

    yield from asyncio.sleep(1)

    print("Asyncio.Task - binomialCoeff (%s,%s) = %s" % (n, k, result))

    if __name__ == "__main__":

    tasks = [asyncio.Task(factorial(10)), asyncio.Task(

    fibonacci(10)), asyncio.Task(binomialCoeff(20, 10))]

    loop = asyncio.get_event_loop()

    loop.run_until_complete(asyncio.wait(tasks))

    loop.close()

    使用Asyncio和Futures

    示例代码如下所示

    import asyncio

    import sys

    @asyncio.coroutine

    def first_coroutine(future, N):

    count = 0

    for i in range(1, N + 1):

    count = count + i

    yield from asyncio.sleep(4)

    future.set_result(

    "first coroutine (sum of N integers) result = " + str(count))

    @asyncio.coroutine

    def second_coroutine(future, N):

    count = 1

    for i in range(2, N + 1):

    count *= i

    yield from asyncio.sleep(3)

    future.set_result("second coroutine (factorial) result = " + str(count))

    def got_result(future):

    print(future.result())

    if __name__ == "__main__":

    N1 = 1

    N2 = 1

    loop = asyncio.get_event_loop()

    future1 = asyncio.Future()

    future2 = asyncio.Future()

    tasks = [

    first_coroutine(future1, N1),

    second_coroutine(future2, N2)

    ]

    future1.add_done_callback(got_result)

    future2.add_done_callback(got_result)

    loop.run_until_complete(asyncio.wait(tasks))

    loop.close()

    分布式编程

    GPU 编程

    相关参考

    内容来源于网络,如有侵权请联系客服删除

    展开全文
  • 浅谈并行编程语言 Unified Parallel C

    千次阅读 2013-01-02 03:57:26
    简介: 随着多核技术的发展,为了提高硬件的利用率和满足超级计算日益增长的需求,并行编程语言应运而生,UPC就是其中之一。越来越多的程序开发人员面临到并行编程的问题,因此学习一门并行编程语言必要性变得愈发...

    国威,软件工程师, IBM

    简介: 随着多核技术的发展,为了提高硬件的利用率和满足超级计算日益增长的需求,并行编程语言应运而生,UPC就是其中之一。越来越多的程序开发人员面临到并行编程的问题,因此学习一门并行编程语言必要性变得愈发迫切。UPC并行编程语言在国外已经得到重用,但是在国内介绍该语言的材料还比较匮乏。因此,本文通过重点介绍 UPC并行编程语言对 C 语言所进行的扩展,使读者对 UPC 并行编程语言具有初步的认识,有利于读者进一步对 UPC并行编程语言的学习。

    发布日期: 2011 1 06 
    级别: 初级
     
    访问情况 21486次浏览
     
    评论: 0 (查看 | 添加评论 -登录)

     平均分 (18个评分)
    为本文评分

     

    并行编程和 Unified Parallel C语言概述

    并行编程介绍

    并行编程是通过同步执行计算机指令的方式来取得比串行编程更快速度的一种编程方法。并行编程是相对于传统的串行编程而提出的概念。在串行编程中,一个程序的指令在单一的 CPU上按照先后顺序依次执行,而在并行编程则将一个程序分成独立的若干部分在一个或多个 CPU上进行同步执行以取得更高的运算效率和性能。

    根据底层的内存结构,并行编程可以分为以下三种程序设计类型:

    • 共享内存模型:多个线程或进程同时运行。它们共享同一内存资源,每个线程或进程都可以访问该内存的任何地方。例如 openMP 就是采用共享内存模型。
    • 分布式内存模型:多个独立处理结点同时工作,每个处理结点都有一个本地的私有内存空间。执行程序的进程可以直接访问其私有内存空间。 若一个进程需要访问另一个处理结点处的私有空间,则此进程需要以发送信息给该进程来进行访问。MPI 就是采用分布式内存模型。
    • 分布式共享内存模型:整个内存空间被分为共有空间和私有空间。每个线程可以访问所有的共有空间,并且每个线程都有自己独立的私有空间。Unified Parallel C 就是采用分割全局地址空间模型。

    Unified Parallel C语言概述

    Unified Parallel C (UPC)是基于分布式共享内存程序设计模型,应用于超级计算机上进行高效能计算的并行编程语言。它提取了 AC, Split C, Parallel C Preprocessor这三种并行语言的优点,对 C 语言(ISO C99 标准)进行扩展,增加了一些特性和概念使之既保留了原来 C语言的简洁高效的优点,同时又可以支持并行编程。可以说 , UPC并行编程语言是 C 语言的超集,任何语法正确的 C 语言在 UPC 中都是正确的。

    为了支持并行编程,UPC C 语言作了以下的扩充:

    • 显式并行执行模型
    • 共享内存空间
    • 同步原语与存储一致性模型
    • 内存管理原语

    回页首

    UPC程序设计模型

    UPC是基于图 1 所示的分布式共享内存程序设计模型的并行程序语言。

    图 1. 分布式共享内存程序设计模型
    1.分布式共享内存程序设计模型
     

    分布式共享内存程序设计模型将全局的内存空间分为共享内存空间和私有内存空间。共享内存空间又在逻辑上被分为一个个的分区。每一个线程对应着一个共享内存分区,我们称这个线程与其对应的共享内存分区之间的逻辑关系为亲缘关系(affinity)。我们称和某线程具有亲缘关系的共享内存为本地共享内存。每个线程对应着何其具有亲缘关系的共享内存分区和自己独有的私有空间。

    每个线程可以访问所有的共享空间和自己的私有内存空间。如图所示,数据 x位于进程 0 所对应的共享空间分区,数据 y位于线程 0 的私有内存空间。线程 0可以访问所有的黄色的共享区间和它自己的私有内存空间。线程 0可以访问数据 y, 却不可访问数据 z, 因为数据 y位于线程 0 的私有内存空间,而数据 z在线程 1 的私有内存空间。线程 1和线程 0 都可以访问数据 x,但是由于线程 0 和数据 x 具有亲缘关系,所以线程 0要比线程 1 访问数据 x速度要快得多。

    亲缘关系在 UPC中是一个非常重要的概念。用线程来访问与其具有亲缘关系的数据可以显著地提高程序的效率。

    回页首

    术语解释

    线程:指运行中的程序的调度单位

    THREADS: UPC中的关键字。它是一个值为整型的表达式,代表参与目前程序参与执行的线程总数。在每个线程中,THREADS具有相同的值。在静态执行环境中,THREADS是一个整型常量。

    MYTHREADSUPC中的关键字。它是一个值为整型的表达式,代表目前正在执行的某一个线程。

    亲缘关系:一个线程与共享数据之间的逻辑关系

    私有数据:位于一个线程是私有内存空间的数据,这个数据只能为该线程所访问。

    共享数据:位于一个线程的共享内存空间的数据,这个数据可以为所有线程所访问。

    共享数组:一个数组其所有的元素均为共享类型

    共享指针:一个位于共享内存空间的指针,它可以指向共享数据或者私有数据。

    私有指针:一个位于私有内存空间的指针,它可以指向共享数据或者私有数据。

    指向共享数据指针:一个指向共享数据的指针,它可以是共享指针,也可以是私有指针。

    指向私有数据指针:一个指向私有数据的指针,它可以是共享指针,也可以是私有指针。

    指向共享数据私有指针:一个位于私有内存空间,指向共享数据的指针。

    指向共享数据共享指针:一个位于共享内存空间,指向私有数据的指针。

    指向私有数据私有指针:一个位于私有内存空间,指向私有数据的指针。

    指向私有数据共享指针:一个位于共享内存空间,指向共享数据的指针。

    回页首

    数据与指针

    共享和私有数据

    UPC将数据分为私有和共享两种类型。UPC C 语言扩展了一个新的数据类型限定词 shared.类型限定词 shared声明数据为共享类型。如果数据声明中没有用到 shared数据类型限定词,则所声明的数据位私有数据。可以说在 C语言中的数据类型在 UPC 中都是私有数据类型。

    当声明一个共享数据类型的时候,该数据会被分配到共享内存空间中。若该共享数据是标量数据,则该数据会被分配到线程 0的共享内存空间中。若该共享数据为数组,则该数据将根据该数组中的布局限定词来分配到共享内存空间中,关于共享限定词,将在 4.2中详细阐述。若该当你声明一个私有数据类型的时候,该数据会被分配到每一个线程的私有内存空间中。


    清单 1.数据声明例子

                                      

     int x;              // 声明了一个私有数据类型

     

     shared int y;      // 声明了一个共享整型数据

     

     shared int z[11]  // 声明了一个共享整型类型数组 , 数组中包含 11个共享整型类型元素

     

    2 显示了以上所声明的数据在内存中的视图

    图 2. 数据声明内存视图

    2.数据声明内存视图
     

    如图 2所示声明语句“int x声明了一个私有整型数据,这个数据被分配到每一个线程的私有空间中。声明语句“shared int y”声明了一个共享整型数据,这个数据被分配到线程 0的共享内存空间中。声明语句“shared int z[11]”声明了一个共享整型数组,该数组的元素如图依次被分配到每一个线程的共享内存空间。

    数组

    UPC的数组声明引入了一个布局类型限定词。布局类型限定词决定多少个连续的共享元素被分配到同一个线程的共享内存空间中。例如 ,下面的声明了一个共享浮点型的数组 A:

    shared [block_size] int A[number_of_elements]

    [block_size]是布局类型限定词,block_size是一个非负的数值,代表区块的大小。例如,当布局类型限定词是 [3]时,则每三个连续的元素为一个区块分配到一个线程的共享内存空间中。假若局类型限定词是下列的情形,区块大小有所不同:

    • 如果布局类型限定词不存在的情况下,则所声明的共享数组按照区块大小为 1 进行分配。
    • 如果布局类型限定词为 [ ] 或者 [0], 说明区块大小为无穷大,则所声明的共享数组将所有元素分配到线程 0 的共享内存空间中。
    • 如果布局类型限定词为 [*], 则所声明的共享数组将按照区块大小为 ( sizeof(array) / upc_elemsizeof(array) + THREADS - 1 ) / THREADS 进行分配。


    清单 2.数组声明例子

                                      

     int A[3];              // 声明了一个私有整型数组 A,有 3个元素。

     

     shared float B[5]    // 声明一个共享浮点型数组 B,有 5个元素,按照区块大小为 1 进行分配。

     

     shared [2] int C[11] // 声明一个共享整型数组 C, 11个元素,按照区块大小为 3 进行分配

     

     shared [] float D[5] // 声明一个共享浮点型数组 D, 5个元素,所有元素分配到线程 0的共享内存空间中。

     

    以上的例子中所声明的数组元素在内存中的分配视图下面图 3所示。

    图 3. 数组元素内存分配视图


    3.数组元素内存分配视图
     

    如图 3所示,语句“int A[3]; ”声明了一个私有整型数组 A,每一个线程都被分配了相同的数组元素。语句“shared float B[5]; ”声明一个共享浮点型数组 B,因为没有布局类型限定词,所有默认按照区块大小为 1进行元素的一次分配。语句“shared [2] int C[11]”声明了一个共享整型数组 C,区块大小为 2,即每 2 个连续的元素会被一次分配到每个线程之中,直至元素分配完毕为止。语句“shared [] float D[5]”声明了一个共享浮点型数组 D,区块大小为无穷大,D数组的所有元素全被分配到线程 0的共享内存空间中。

    指针

    因为 UPC中具有共享和私有两种数据类型,所以一个指针所指向的数据可能是共享或者私有数据类型,并且指针的本身可能是共享或者私有数据类型,因此 UPC中共有 4 种指针类型如下面图 4所示。红色箭头代表的是 指向共享数据共享指针,黑色箭头代表的是指向私有数据共享指针,蓝色箭头代表的是 指向共享数据私有指针,绿色箭头代表的是 指向私有数据私有指针

    图 4. UPC 四种指针种类


    4. UPC四种指针种类
     

    UPC的四种指针在内存中的视图如下面图 5所示。

    图 5. UPC 指针内存视图
    5. UPC指针内存视图
     

    • p1 是指向私有数据私有指针。所有 C 语言中的指针都是这种类型指针,特点是速度快。如果一个指向共享数据指针和所指的数据有亲缘关系,您可以将该指针转化为 p1 类型指针来提高访问速度。
    • p2 是指向共享数据私有指针。这个指针可以用来访问共享数据。p2 速度要比 p1 慢。p2 访问和它有亲缘关系的共享数据速度比访问和它没有亲缘关系的共享数据的速度快。
    • p3 是指向私有数据共享指针。这种指针会是线程访问其他线程的私有数据,不建议使用。
    • p4 是指向共享数据共享指针。


    清单 3.指向私有数据私有指针声明

                                      

     int *p1;   // 声明了一个指向私有整型数据的私有指针。这个指针将被分配到每一个线程的私有内存中

     shared int *p2 // 声明一个指向共享数据私有指针,这个指针将被分配到每一个线程的私有内存中

     int * shared p3// 声明一个指向私有数据共享指针,不建议使用,这个指针被分配到线程 0 的共享内存中

     shared int * shared p4// 声明了一个指向共享数据共享指针,这个指针被分配到线程 0 的共享内存中

     

    操作符

    UPC C 的基础上扩展了以下的 5个一元运算符。这些运算符的操作数可以是一个类型或者是一元表达式。

    • & 返回指向某一个操作数的指针。如果这个操作数是一个 T 类型的共享数据,则返回值具有 shared [] *T 的类型。
    • sizeof 返回一个操作数字节大小。
    • upc_blocksizeof 返回一个操作数的区块大小,这个值即是在类型声明中的布局类型限定词中的值。
    • upc_elemsizeof 返回非数组的最左边类型的字节大小。
    • upc_localsizeof 返回一个共享类型或者共享数据内和当前执行的线程有亲缘关系的字节大小。

    回页首

    upc_forall语句

    多线程的并行编程中,任务在不同线程之间的分配是通过 upc_forall语句实现的。upc_forall语句的原型是 upc_forall (表达式 1;表达式 2;表达式 3;亲缘关系表达式 )upc_forall语句与 C 语言中的 for语句的区别就是增加了第四个参数亲缘关系表达式。upc_forall语句中的前三个表达式在语法上与 C语言中的 for 语句一样。

    亲缘关系表达式根据以下的规则来给线程分配任务:

     

    • 如果亲缘关系表达式是一个整型表达式,则由线程 MYTHREAD == affinity % THREADS 来执行当前的循环体。
    • 如果亲缘关系表达式是一个指向共享数据指针类型的表达式,则由线程 MYTHREAD == upc_threadof(affinity) 来执行当前的循环体。
    • 若亲缘关系表达式不存在或者为关键字 continue,则每个线程执行当前的循环体。


    清单 4. upc_forall语句例子

                                      

     // 假定 THREADS=5

     upc_forall(i=0;i<9;i++;i)

     // i=5的时候,MYTHREAD=7%5=2,则由线程 2 来执行以下循环体。

     {

     }

     

     upc_forall(i=0;i<9;i++;&A[i]) 

     //A 是一个共享类型数组,则由线程 MYTHREAD=upc_threadof(affinity),即

      // A[i]这个共享类型元素有亲缘关系的线程,来执行当前的循环体。

     {

     }

     

     upc_forall(i=0;i<9;i++;continue) //每个线程执行当前的循环体。

     {

     }

     

    回页首

    存储一致性模型

    在一个多线程的程序中,一个线程对共享类型数据的访问顺序在其他线程看来可能是不同的。多个线程同时对于同一块共享内存空间的读和写,可能会导致其他线程读取过期的,半更新的,或者已经更新的数据。存储一致性模型则定义了读操作将以什么样的顺序看到写操作的结果。通过指定存储一致性模型,编程人员可以决定一个线程对共有数据的更新什么时候对其他线程可见。

    数据的存储一致性模型被分为 strict shared 两种类型:

    • strict 存储一致性模型

    一个线程对共享数据的更新对其他线程立即可见。任何对共享数据的操作只有在先前对该共享数据的操作完成之后才能进行。编译器不可以改变独立的对共享数据访问的顺序来进行程序性能优化。运用这种存储一致性模型将延长程序的执行时间。

    • relaxed 存储一致性模型

    一个线程可以在任何时间对一个共享数据进行操作,无论其他线程对此共享数据进行任何的操作。编译器可以通过改变独立的对共享数据访问的顺序来实现程序性能的优化。

    程序员可以对三种不同层次来定义存储一致性模型 ,这些不同定义方式的优先级由高到底排列如下:

    1. 用关键词 strict 或者 shared 来对一个变量进行定义存储一致性模型。
    2. 用 UPC 指令 #pragma upc strict 或者 #pragma upc relaxed 对一个程序区块进行定义存储一致性模型。
    3. 用头文件 include <upc_strict.h> 或者 include <upc_relaxed.h> 来对整个程序的范围进行定义存储一致性模型。

    回页首

    同步

    UPC提供了障碍,篱笆和锁这三种同步原语来控制线程之间的交互作用。

    障碍(barrier)是用来实现线程之间同步的原语。障碍又分为阻挡障碍(blocking barrier)和非阻挡障碍(non-blocking barrier)。

    阻挡障碍如图 6所示,线程 1 和线程 2 以不同的速度在执行程序,当线程 1遇到阻挡障碍语句 upc_barrier的时候会停下来等待线程 2。当线程 2也执行到该阻挡障碍语句 upc_barrier的时候,线程 1 才会继续向前执行。

    图 6. 阻挡障碍示意图


    6.阻挡障碍示意图
     

    非阻挡障碍由 upc_notify upc_wait 组成。如图 7 所示,线程 1和线程 2 以不同的速度在执行程序,当线程 1遇到非阻挡障碍语句 upc_notify的时候会通知线程 2 它已经执行到 upc_notify 的这个程序点了,然后线程 1 会继续执行,当执行到 upc_wait 才停下来等待线程 2,一直等到线程 2也执行到该 upc_notify的程序点发出到达报告,这时候线程 1才继续向前执行。一般在 upc_notify upc_wait 之间,程序员可以让线程进行无关其他线程的运算,这样充分地利用了线程 1的等待时间,提高了程序的性能。

    图 7. 非阻挡障碍示意图


    7.非阻挡障碍示意图
     

    篱笆(upc_fence)是用来实现线程内的同步的原语。当一个线程执行遇到篱笆的时候,它会确保所有在篱笆之前对共享数据的访问全部完成之后,再执行在篱笆之后对共享数据的访问。

    锁(lock在多线程的环境中,多个线程同时对一个共享数据的操作,会造成竞争状态。一个线程对一个共享数据的修改可能会使得其他线程读到不正确的数据。锁用来确保在同一的时间内只能有一个线程进行访问某一共享数据。锁是以牺牲程序性能来保证程序的正确性,因为它阻止了多线程的同步执行,所以在保证程序的正确性的基础上,尽量避免锁的使用。请参见使用库函数中的锁

    回页首

    UPC库函数

    UPC语言中有实用函数和集体函数两类库函数。所谓集体函数就是由所有线程调用,有同样的返回值。所谓非集体函数就是由一个线程调用的函数,如果被多线程调用,则有不同的返回值。实用函数可以分为以下 5类:


    程序终止

                                      

     upc_global_exit 释放内存,结束所有线程的程序运行。

     

    动态内存分配

    1. upc_all_alloc 是集体函数,给每一个的线程分配共享内存空间 .\
    2. upc_global_alloc 是非集体函数,给每一个的线程分配共享内存空间 .
    3. upc_alloc 给调用该函数的线程分配和其有亲缘关系的共享内存空间。
    4. upc_free 释放动态分配的共享内存空间。

    指向共享数据指针操作

    1. upc_addrfiled 返回参数所指向的数据的本地地址
    2. upc_affinitysize 返回在一个共享类型数据中和给定的某一个线程有亲缘关系的共享内存空间大小。
    3. upc_phaseof 返回一个参数所指定的共享数据在其所在的区块中的位置。
    4. upc_resetphase 返回一个与所给参数指针相同的新指针,并且这个新的指针的相恒为 0。
    5. upc_threadof 返回一个与参数所指定的共享数据有亲缘关系的线程指数。

    1. upc_all_lock_alloc 集体函数,给所有调用该函数的线程分配同一个锁,初始状态为解除锁定状态,并返回一个指向该锁的指针到每个线程。
    2. upc_global_lock_allock 非集体函数,给调用该函数的线程分配以个锁,初始状态为解除锁定状态,并返回指向该锁的指针。
    3. upc_lock 将参数所指向的锁的状态设定为锁定状态。
    4. upc_lock_attepmt 试图将参数所指向的锁的状态设定为锁定状态,成功则返回 1,失败则返回 0。
    5. upc_unlock 将参数所指定的锁的状态设定为解除锁定状态。
    6. upc_lock_free 释放参数所指向的锁占用的内存空间。

    内存转储

    1. upc_memcpy 把数据从一个共享内存空间复制到另外一个共享内存空间。
    2. upc_memget 把和某个线程本地的共享数据复制到调用该函数的一个私有内存空间中
    3. upc_memput 把调用该函数的线程内的私有数据复制到和另一个线程的本地共享内存空间中。
    4. upc_memset 将一个字符复制到指定的共享内存空间中。

    集体函数可以分为以下两类:

    数据移动函数

    1. upc_all_broadcast 将某个线程上的一块共享内存空间复制到每一个线程的共享内存空间。
    2. upc_all_scatter 将某个线程上的第 i 块共享内存空间复制到第 i 个线程上的本地共享内存空间。
    3. upc_all_gather 将第 i 个线程上的一块本地共享内存空间复制到某一个线程的第 i 块共享内存空间。
    4. upc_all_gather_all 将第 i 个线程上的一块本地共享内存空间复制到每一个线程的第 i 块共享内存空间。
    5. upc_all_exchange 将线程 j 上第 i 块本地共享内存空间复制到线程 i 上第 j 块本地共享内存空间。
    6. upc_all_permute

    计算操作函数

    下面的这两个库函数都是用来对所有元素进行用户定义的操作(加减乘除等等),然后将所得结果返回到一个线程。

    1. upc_all_reduce
    2. up_all_prefix_reduce

    回页首

    UPC应用程序实例

    下面通过一个具体的 UPC程序的例子来更好地理解 UPC语言 . 这个例子是通过 upc_forall语句对线程进行分工,计算出所给数组 A所有元素之和。程序输出如图 8所示。


    清单 5. UPC程序例子

                                      

     #include <upc.h>  // 假定设定 4 个线程来执行这个程序

     

     # define N 10000

     

     shared int A[N];

     shared int sum=0;

     shared int partialsum[THREADS]={0};

     /* 声明一个共享整型数组,具有 THREADS个元素,因为我们假定四个线程来运行该程序,

     所以 THREADS的值为 4。这个数组用来记录各个线程所算出来的元素和。

       */

     

     upc_lock_t *lock;                    

       // 声明一个类型为 upc_lock_t 的指针

     

     int main()

     {

         lock=upc_all_lock_alloc();       

         // 给每一个线程分配一个共同的锁,并将其地址分配给 lock 指针

         upc_forall(int i=0;i<N;i++;&A[i])

         //upc_forall 语句根据亲缘关系表达式给线程分工

         {

            A[i]=i;        // 将数组 A 的元素初始化

         }

         upc_barrier; // 阻挡障碍确保每个线程都完成对数组 A 的元素初始化

     

         if(lock != NULL) // 如果成功地分配了这个锁

         {

             upc_forall(int i=0;i<N;i++;&A[i])

             // 各个线程计算自己本地分配的数组元素的和

             {

                partialsum[MYTHREAD] +=A[i]; 

             }

             upc_barrier; // 阻挡障碍确保每个线程完成计算,再向前执行。

     

             upc_lock(lock);

             /* 这里到了程序的关键部分,因为 sum 是一个共享数据,可以被所有线程同时操作,

                所以这里加个锁,只允许同一个时间内一个线程来操作 sum变量,从而避免了竞争状态,

              保证了程序的正确性 */

             sum +=partialsum[MYTHREAD];

             // 将所有线程的分别计算出来的和加起来就是数组 A 所有元素的和                                 

             upc_unlock(lock);  // 开锁

     

             upc_barrier;       

             // 阻挡障碍确保每个线程计算出来的和都加到 sum 上,再向前执行

     

             if(MYTHREAD == 0) // 如果执行该语句的是线程 0 的话,则输出计算结果

             {

                 upc_lock_free(lock);  //释放分配的锁

                 printf("Th:%d, result=%d \n",MYTHREAD,sum);

                 return 1;

             }

         }

         return 0;

     }



    8.程序输出结果

    图 8. 程序输出结果
     

    回页首

    IBM XL Unified Parallel C编译器简介

    IBM® XL Unified Parallel C编译器支持 Unified Parallel C语言标准 1.2,可以应用在运行 AIX® Linux® 操作系统的 IBM power 系类服务器上。该编译器是继承了 IBM XL系列的编译器的优越性能,提供了详尽的语义和句法的检查,并对程序进行多种 UPC语言特有的优化。

    回页首

    结语

    本文旨在介绍 UPC的入门基础,让读者对并行编程语言有初步的认识。对于 UPC语言的细节上的语法和使用方法请参阅 UPC标准 1.2.

     

    参考资料

    学习

    讨论

    关于作者

    胡国威,IBM CDL软件工程师,所在团队目前从事 Rational compiler产品相关用户文档写作。他拥有计算机本科学位和英语专业硕士学位,是 XL Unified Parallel C User ’ s Guide的主要作者,对并行编程语言 UPC有着浓厚的兴趣。

     

     

    来源:http://www.ibm.com/developerworks/cn/linux/l-cn-upc/
    展开全文
  • 很多人不是特别明白并发编程和并行编程的区别所在,有很多人很容易搞混淆,觉得二者近似相等,本文将用几个浅显易懂的例子,来说明一下什么是并发和并行。1、任务与多任务关于什么是进程,什么是线程,这里不打算多...

    很多人不是特别明白并发编程和并行编程的区别所在,有很多人很容易搞混淆,觉得二者近似相等,本文将用几个浅显易懂的例子,来说明一下什么是并发和并行。

    1、任务与多任务

    关于什么是进程,什么是线程,这里不打算多说,关于每一种开发语言的多线程处理技术语法上有所区别,原理很多类似,可以查阅相关的参考书。什么是任务呢?其实很简单,在我们生活中,比如我今天要学一首歌,要在操场上散步1000米,然后还要向老师电话汇报研究成果,这是今天的三个任务。在编程语言中,实现某一个任务的一般单元就是“函数”。

    2、顺序执行

    上面三个任务,如果在一般的单线程执行的程序里面,分别定义三个函数,然后依次顺序执行,即执行完第一个在执行第二个,然后执行第三个,即我先把一首歌学会,然后在操场上散步1000米,然后再给老师汇报,这样做效率低下。我们可以有一个更加高效的办事方法,我在散步的时候,顺便打电话给老说汇报一下今天的成果,然后边散步边听歌,便跟着学,这样效果就高很多,这里的是“并行执行”。

    3、并发和并行的一些理解——concurrency  and  parallellism

    并发和并行都是完成多任务更加有效率的方式,但还是有一些区别的,并发(concurrency),并行(parallellism),可见他们的确是有区别的。下面通过一些具体的例子进行说明。

    例子一:

    假设一个有三个学生需要辅导作业,帮每个学生辅导完作业是一个任务

    顺序执行:老师甲先帮学生A辅导,辅导完之后再取给B辅导,最后再去给C辅导,效率低下 ,很久才完成三个任务

    并发:老师甲先给学生A去讲思路,A听懂了自己书写过程并且检查,而甲老师在这期间直接去给B讲思路,讲完思路再去给C讲思路,让B自己整理步骤。这样老师就没有空                         着,一直在做事情,很快就完成了三个任务。与顺序执行不同的是,顺序执行,老师讲完思路之后学生在写步骤,这在这期间,老师是完全空着的,没做事的,所以效率低下。

    并行:直接让三个老师甲、乙、丙三个老师“同时”给三个学生辅导作业,也完成的很快。

    例子二:

    顺序执行:你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。

    并发:你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。

    并行:你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并

    理解:

    解释一:并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。

    解释二:并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。

    解释三:在一台处理器上“同时”处理多个任务,在多台处理器上同时处理多个任务。如hadoop分布式集群

    普通解释:

    并发:交替做不同事情的能力

    并行:同时做不同事情的能力

    专业术语:

    并发:不同的代码块交替执行

    并行:不同的代码块同时执行

    并发和并行的意义:

    并发和并行都可以处理“多任务”,二者的主要区别在于是否是“同时进行”多个的任务。

    但是 涉及到任务分解(有先后依赖的任务就不能做到并行)、任务运行(可能要考虑互斥、锁、共享等)、结果合并

    ---------------------

    作者:LoveMIss-Y

    来源:CSDN

    原文:https://blog.csdn.net/qq_27825451/article/details/78850336

    版权声明:本文为博主原创文章,转载请附上博文链接!

    展开全文
  • 1、基本概念多进程库提供了Pool类来实现简单的多进程任务。Pool类有以下方法:- apply():...- map():这是内置的map函数的并行版本,在得到结果之前一直阻塞,此方法将可迭代的数据的每一个元素作为进程池的一个...
  • 什么是Python中的并行和并发发布时间:2020-08-25 16:19:56来源:亿速云阅读:104作者:Leah这篇文章将为大家详细讲解有关什么是Python中的并行和并发,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读...
  • Pythpn并发编程——多线程与协程目录Pythpn并发编程——多线程与协程1. 进程与线程1.1 概念上1.2 多进程与多线程——同时执行多个任务2. 并发和并行3. Python多线程——futures3.1 多线程用法3.2. 为什么多线程每次...
  • 为多核环境创造并行编程语言

    千次阅读 2011-05-03 15:10:00
    英特尔预计,通过使用 C++ 等编程语言,性能损失已经达到20%至30%,如果编写代码能够获得生产力优势,则这一点可以接受。通过并行编程,性能开销可能在30%左右。但是通过多个内核,整体性能却可以提高多达 6 倍。  ...
  • Futhark是ML系列中的纯功能数据并行编程语言。 可以将其编译为通常非常高效的并行代码,在CPU或GPU上运行。 该语言和编译器最初是一部分,由哥本哈根大学的开发。 该语言和编译器非常稳定,适合于实际编程。 有关...
  • 1.目前两种重要的并行编程模型是数据并行和消息传递,数据并行模型的编程级别高,编程相对简单,但是它仅仅适用于数据并行问题;消息传递模型编程级别低,但具有更加广泛的扩展性。 2.数据并行模型即将相同的操作...
  • 1 语言分类编程语言俗称“计算机语言”,种类非常的多,总的来说可以分成机器语言、汇编语言、高级语言三大类。电脑每做的一次动作,一个步骤,都是按照已经用计算机语言编好的程序来执行的,程序是计算机要执行的...
  • ant是一种高效率的面向对象的并行多种语言,目标是多机分布式并行编程像单机串行编程一样高效简单丝滑。通过简单的编程和程序执行,即可以替换的调度成千上万台计算机并行执行程序并且不用在意向并行带来的执行顺序...
  • 并行编程模式

    2018-12-29 13:54:50
    本书介绍了并行编程模式的相关概念和技术,主要内容包括并行编程模式语言、并行计算的背景、软件开发中的并发性、并行算法结构设计、支持结构、设计的实现机制以及OpenMP、MPI等。
  • 由北京并行科技股份有限公司与中科院计算所西部高等技术研究院联合主办、北京中科图云科技有限公司与北京安控冠博科技有限公司协办的“多语言框架高性能GPU并行编程--C/C++,Matlab,Python”培训将于2019年12月14日...
  • 并行编程平台及语言

    千次阅读 2017-12-29 10:34:19
    OpenCL: cpu(thread, SMID) gpu dsp CUDA: nvidia gpu OpenGL:gpu OpenMP:cpu(thread) using "pragma" OpenACC:cpu gpu using "pragma" C++ AMP:gpu (implemented on DirectX 11) OpenHMPP:hardware accelerat
  • 在程序编写的复杂程度上,OpenMP只需在串行程序上做少许的改动即可实现并行,而MPI的编程往往比较复杂,一般与串行程序是两套不同的代码。本文简单介绍Fortran语言下使用OpenMP实现并行的方法。话不多说,直接上一个...
  • 本文内容来自《R 语言编程艺术》(The Art of R Programming),有部分修改不要对并行抱有太高期望,很多情况下并行版本实际上比串行版本运行速度更慢。共同外链问题mutual outlink对于 n x n 矩阵sum = 0for i = 0......
  • 首先MPI并行编程要引入头文件,也就是函数库 #include "mpi.h" 先讲解几个简单的函数,然后再用这几个函数写一下并行输出hellowrld MPI_Init(&argc,&argv);这个函数的作用就是作为初始化.
  • M P I 并行编程 C/C ++/Fortran 语言绑定 孔涛 山东大学数学学院 版本v1.2 2011/3/23 1 K.T. , SDU tkong@ Parallel Programming with MPI Binding with C/C++/Fortran Kong Tao School of Maths,Shandong Univ. ...
  • 国内顶尖的并行计算领域知名专家风辰多年实践经验总结,简洁明了,辅以大量示例,全面介绍主流硬件平台上的向量化库,并行编程语言的设计细节,OpenCL程序在硬件平台的映射与执行,并给出多个性能优化实战案例。...
  • 并行编程模型

    千次阅读 2017-03-12 17:19:33
    在计算领域,并行编程模型是并行计算机体系架构的一种抽象,它便于编程...并行编程模型的实现有两种方式,作为已有的语言的一种扩展,通过库的形式来调用,或者作为一种全新的语言。 围绕一个实际的编程模型的共识是重
  • 1. 语言本身不具备并行性,但
  • 在这里,JAXenter与Ateji创始人Patrick Viry进行了交谈,讨论了Java多核和并行编程的新语言扩展和编程环境,以及所谓的“多核危机”。 JAXenter:什么是Ateji PX? Patrick Viry: Ateji PX是Java语言的扩展,...
  • 并行编程

    2018-03-20 23:06:30
    Python语言参考 主要学习内容: 1. 多进程(启动多个自身进程、启动另外的进程、进程通信、分布式进程-即多机器); 2. 多线程(产生多线程、锁): GIL导致它只能单核,所以多线程并发不可行。也就不用怎么学...
  • 本书共7章:第1章主要介绍并行编程的基本概念与并行计算的基础理论,第2章主要介绍并行循环的知识,第3章介绍并行任务处理,第4章阐述并行合并计算的机理,第5章介绍future模式,第6章在前文的基础上深入探讨动态...
  • 并行编程概览对绝大多数人而言,编程语言只是一个工具,讲究简单高效。科学家的主要精力应该用在科研创新活动上,编程工作仅仅是用来验证创新的理论,编程水平再高也不可能获得诺贝尔奖。对学生和企业程序员而言,.....
  • python并行编程 中文版

    2018-08-24 11:46:34
    本章最后简单介绍了Python编程语言。Pyhton的 易用和易学、可扩展性和丰富的库以及应用,让它成为了一个全能性的工具,当然,在并行计算方面也得 心应手。最后结合在Python中的应用讲了线程和进程。解决一个大问题...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,508
精华内容 1,403
关键字:

并行编程语言