00001
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081 #include "machine-smp.h"
00082 #include "sockRoutines.h"
00083
00084 void CmiStateInit(int pe, int rank, CmiState state);
00085 void CommunicationServerInit(void);
00086
00087 static struct CmiStateStruct Cmi_default_state;
00088
00089 #define CMI_NUM_NODE_BARRIER_TYPES 2
00090 #define CMI_NODE_BARRIER 0
00091 #define CMI_NODE_ALL_BARRIER 1
00092
00093
00094
00095 #if CMK_SHARED_VARS_NT_THREADS
00096
00097 CmiNodeLock CmiMemLock_lock;
00098 #ifdef CMK_NO_ASM_AVAILABLE
00099 CmiNodeLock cmiMemoryLock;
00100 #endif
00101 static CmiNodeLock comm_mutex;
00102 #define CmiCommLockOrElse(x)
00103 #define CmiCommLock() (CmiLock(comm_mutex))
00104 #define CmiCommUnlock() (CmiUnlock(comm_mutex))
00105
00106 static DWORD Cmi_state_key = 0xFFFFFFFF;
00107 static CmiState Cmi_state_vector = 0;
00108
00109 #if 0
00110 # define CmiGetState() ((CmiState)TlsGetValue(Cmi_state_key))
00111 #else
00112 CmiState CmiGetState(void)
00113 {
00114 CmiState result;
00115 result = (CmiState)TlsGetValue(Cmi_state_key);
00116 if(result == 0) {
00117 return &Cmi_default_state;
00118
00119 }
00120 return result;
00121 }
00122 #endif
00123
00124 void CmiYield(void)
00125 {
00126 Sleep(0);
00127 }
00128
00129 #define CmiGetStateN(n) (Cmi_state_vector+(n))
00130
00131 void CommunicationServerThread(int sleepTime);
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154 static DWORD WINAPI call_startfn(LPVOID vindex)
00155 {
00156 int index = (int)(intptr_t)vindex;
00157
00158 CmiState state = Cmi_state_vector + index;
00159 if(Cmi_state_key == 0xFFFFFFFF) PerrorExit("TlsAlloc");
00160 if(TlsSetValue(Cmi_state_key, (LPVOID)state) == 0) PerrorExit("TlsSetValue");
00161
00162 ConverseRunPE(0);
00163 #if 0
00164 if (index<_Cmi_mynodesize)
00165 ConverseRunPE(0);
00166 else {
00167 CommunicationServerInit();
00168 if (Cmi_charmrun_fd!=-1)
00169 while (1) CommunicationServerThread(5);
00170 }
00171 #endif
00172 return 0;
00173 }
00174
00175
00176
00177
00178
00179
00180
00181 static volatile LONG entered_barrier_count[CMI_NUM_NODE_BARRIER_TYPES] = {0};
00182 static volatile LONG exited_barrier_count[CMI_NUM_NODE_BARRIER_TYPES] = {0};
00183 static HANDLE entrance_semaphore[CMI_NUM_NODE_BARRIER_TYPES];
00184 static HANDLE exit_semaphore[CMI_NUM_NODE_BARRIER_TYPES];
00185
00186
00187
00188
00189 void CmiNodeBarrierCount(int nThreads, uint8_t mode)
00190 {
00191 LONG prev;
00192 if (InterlockedIncrement(&entered_barrier_count[mode]) < nThreads)
00193 WaitForSingleObject(entrance_semaphore[mode], INFINITE);
00194 else {
00195 exited_barrier_count[mode] = 0;
00196 ReleaseSemaphore(entrance_semaphore[mode], nThreads-1, &prev);
00197 }
00198 if (InterlockedIncrement(&exited_barrier_count[mode]) < nThreads)
00199 WaitForSingleObject(exit_semaphore[mode], INFINITE);
00200 else {
00201 entered_barrier_count[mode] = 0;
00202 ReleaseSemaphore(exit_semaphore[mode], nThreads-1, &prev);
00203 }
00204 }
00205
00206 static void CmiStartThreads(char **argv)
00207 {
00208 int i,tocreate;
00209 DWORD threadID;
00210 HANDLE thr;
00211
00212 CmiMemLock_lock=CmiCreateLock();
00213 comm_mutex = CmiCreateLock();
00214 for (i=0; i < CMI_NUM_NODE_BARRIER_TYPES; i++) {
00215 entrance_semaphore[i] = CreateSemaphore(NULL, 0, _Cmi_mynodesize+1, NULL);
00216 exit_semaphore[i] = CreateSemaphore(NULL, 0, _Cmi_mynodesize+1, NULL);
00217 }
00218 #ifdef CMK_NO_ASM_AVAILABLE
00219 cmiMemoryLock = CmiCreateLock();
00220 if (CmiMyNode()==0) printf("Charm++ warning> fences and atomic operations not available in native assembly\n");
00221 #endif
00222
00223 Cmi_state_key = TlsAlloc();
00224 if(Cmi_state_key == 0xFFFFFFFF) PerrorExit("TlsAlloc main");
00225
00226 Cmi_state_vector =
00227 (CmiState)calloc(_Cmi_mynodesize+1, sizeof(struct CmiStateStruct));
00228
00229 for (i=0; i<_Cmi_mynodesize; i++)
00230 CmiStateInit(i+Cmi_nodestart, i, CmiGetStateN(i));
00231
00232
00233 CmiStateInit(_Cmi_mynode+CmiNumPes(),_Cmi_mynodesize,CmiGetStateN(_Cmi_mynodesize));
00234
00235 #if CMK_MULTICORE || CMK_SMP_NO_COMMTHD
00236 if (!Cmi_commthread)
00237 tocreate = _Cmi_mynodesize-1;
00238 else
00239 #endif
00240 tocreate = _Cmi_mynodesize;
00241 for (i=1; i<=tocreate; i++) {
00242 if((thr = CreateThread(NULL, 0, call_startfn, (LPVOID)(intptr_t)i, 0, &threadID))
00243 == NULL) PerrorExit("CreateThread");
00244 CloseHandle(thr);
00245 }
00246
00247 if(TlsSetValue(Cmi_state_key, (LPVOID)Cmi_state_vector) == 0)
00248 PerrorExit("TlsSetValue");
00249 }
00250
00251 static void CmiDestroyLocks(void)
00252 {
00253 CmiDestroyLock(comm_mutex);
00254 comm_mutex = 0;
00255 CmiDestroyLock(CmiMemLock_lock);
00256 CmiMemLock_lock = 0;
00257 for (int i=0; i < CMI_NUM_NODE_BARRIER_TYPES; i++) {
00258 CloseHandle(entrance_semaphore[i]);
00259 CloseHandle(exit_semaphore[i]);
00260 }
00261 #ifdef CMK_NO_ASM_AVAILABLE
00262 CmiDestroyLock(cmiMemoryLock);
00263 #endif
00264 }
00265
00266
00267 #elif CMK_SHARED_VARS_POSIX_THREADS_SMP
00268
00269 CmiNodeLock CmiMemLock_lock;
00270 #ifdef CMK_NO_ASM_AVAILABLE
00271 CmiNodeLock cmiMemoryLock;
00272 #endif
00273 int _Cmi_sleepOnIdle=0;
00274 int _Cmi_forceSpinOnIdle=0;
00275 extern int _cleanUp;
00276 extern void CharmScheduler(void);
00277
00278 #if CMK_HAS_TLS_VARIABLES && !CMK_NOT_USE_TLS_THREAD
00279 static CMK_THREADLOCAL struct CmiStateStruct Cmi_mystate;
00280 static CmiState *Cmi_state_vector;
00281
00282 CmiState CmiGetState(void) {
00283 return &Cmi_mystate;
00284 }
00285 #define CmiGetStateN(n) Cmi_state_vector[n]
00286
00287 #else
00288
00289 static pthread_key_t Cmi_state_key=(pthread_key_t)(-1);
00290 static CmiState Cmi_state_vector;
00291
00292 #if 0
00293 #define CmiGetState() ((CmiState)pthread_getspecific(Cmi_state_key))
00294 #else
00295 CmiState CmiGetState(void) {
00296 CmiState ret;
00297 if (Cmi_state_key == (pthread_key_t)(-1)) return &Cmi_default_state;
00298 ret=(CmiState)pthread_getspecific(Cmi_state_key);
00299 return (ret==NULL)? &Cmi_default_state : ret;
00300 }
00301
00302 #endif
00303 #define CmiGetStateN(n) (Cmi_state_vector+(n))
00304 #endif
00305
00306
00307 #if !CMK_USE_LRTS
00308 #if CMK_HAS_SPINLOCK && CMK_USE_SPINLOCK
00309 CmiNodeLock CmiCreateLock(void)
00310 {
00311 CmiNodeLock lk = (CmiNodeLock)malloc(sizeof(pthread_spinlock_t));
00312 _MEMCHECK(lk);
00313 pthread_spin_init(lk, 0);
00314 return lk;
00315 }
00316
00317 void CmiDestroyLock(CmiNodeLock lk)
00318 {
00319 pthread_spin_destroy(lk);
00320 free((void*)lk);
00321 }
00322 #else
00323 CmiNodeLock CmiCreateLock(void)
00324 {
00325 CmiNodeLock lk = (CmiNodeLock)malloc(sizeof(pthread_mutex_t));
00326 _MEMCHECK(lk);
00327 pthread_mutex_init(lk,(pthread_mutexattr_t *)0);
00328 return lk;
00329 }
00330
00331 void CmiDestroyLock(CmiNodeLock lk)
00332 {
00333 pthread_mutex_destroy(lk);
00334 free(lk);
00335 }
00336 #endif
00337 #endif //CMK_USE_LRTS
00338
00339 void CmiYield(void) { sched_yield(); }
00340
00341 int barrier = 0;
00342 pthread_cond_t barrier_cond = PTHREAD_COND_INITIALIZER;
00343 pthread_mutex_t barrier_mutex = PTHREAD_MUTEX_INITIALIZER;
00344
00345 void CmiNodeBarrierCount(int nThreads, uint8_t mode)
00346 {
00347 static unsigned int volatile level = 0;
00348 unsigned int cur;
00349 pthread_mutex_lock(&barrier_mutex);
00350 cur = level;
00351
00352 barrier++;
00353 if(barrier != nThreads) {
00354
00355 while (cur == level)
00356 pthread_cond_wait(&barrier_cond, &barrier_mutex);
00357 }
00358 else{
00359 barrier = 0;
00360 level++;
00361 pthread_cond_broadcast(&barrier_cond);
00362 }
00363 pthread_mutex_unlock(&barrier_mutex);
00364 }
00365
00366 static CmiNodeLock comm_mutex;
00367
00368 #define CmiCommLockOrElse(x)
00369
00370 #if 1
00371
00372 # define CmiCommLock() CmiLock(comm_mutex)
00373 # define CmiCommUnlock() CmiUnlock(comm_mutex)
00374 #else
00375
00376 static int comm_mutex_isLocked=0;
00377 void CmiCommLock(void) {
00378 if (comm_mutex_isLocked)
00379 CmiAbort("CommLock: already locked!\n");
00380 CmiLock(comm_mutex);
00381 comm_mutex_isLocked=1;
00382 }
00383 void CmiCommUnlock(void) {
00384 if (!comm_mutex_isLocked)
00385 CmiAbort("CommUnlock: double unlock!\n");
00386 comm_mutex_isLocked=0;
00387 CmiUnlock(comm_mutex);
00388 }
00389 #endif
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407 void StartInteropScheduler(void);
00408 void CommunicationServerThread(int sleepTime);
00409
00410 static void *call_startfn(void *vindex)
00411 {
00412 size_t index = (size_t)vindex;
00413 #if CMK_HAS_TLS_VARIABLES && !CMK_NOT_USE_TLS_THREAD
00414 if (index<_Cmi_mynodesize)
00415 CmiStateInit(index+Cmi_nodestart, index, &Cmi_mystate);
00416 else
00417 CmiStateInit(_Cmi_mynode+CmiNumPes(),_Cmi_mynodesize,&Cmi_mystate);
00418 Cmi_state_vector[index] = &Cmi_mystate;
00419 #else
00420 CmiState state = Cmi_state_vector + index;
00421 pthread_setspecific(Cmi_state_key, state);
00422 #endif
00423
00424 ConverseRunPE(0);
00425
00426 if(CharmLibInterOperate) {
00427 while(1) {
00428 if(!_cleanUp) {
00429 StartInteropScheduler();
00430 CmiNodeAllBarrier();
00431 } else {
00432 if (CmiMyRank() == CmiMyNodeSize()) {
00433 while (ckExitComplete.load() == 0) { CommunicationServerThread(5); }
00434 } else {
00435 CsdScheduler(-1);
00436 CmiNodeAllBarrier();
00437 }
00438 break;
00439 }
00440 }
00441 }
00442
00443 #if 0
00444 if (index<_Cmi_mynodesize)
00445 ConverseRunPE(0);
00446 else
00447 {
00448 CommunicationServerInit();
00449 if (Cmi_charmrun_fd!=-1)
00450 while (1) CommunicationServer(5,COM_SERVER_FROM_SMP);
00451 }
00452 #endif
00453 return 0;
00454 }
00455
00456 #if CMK_BLUEGENEQ && !CMK_USE_LRTS
00457
00458 void PerrorExit(const char*);
00459 #endif
00460
00461 #if CMK_CONVERSE_PAMI
00462
00463 pthread_t *_Cmi_mypidlist;
00464 #endif
00465
00466 static void CmiStartThreads(char **argv)
00467 {
00468 pthread_t pid;
00469 size_t i;
00470 int ok, tocreate;
00471 pthread_attr_t attr;
00472 int start, end;
00473
00474 MACHSTATE(4,"CmiStartThreads")
00475 CmiMemLock_lock=CmiCreateLock();
00476 _smp_mutex = CmiCreateLock();
00477 #if defined(CMK_NO_ASM_AVAILABLE) && CMK_PCQUEUE_LOCK
00478 cmiMemoryLock = CmiCreateLock();
00479 if (CmiMyNode()==0) printf("Charm++ warning> fences and atomic operations not available in native assembly\n");
00480 #endif
00481
00482 #if ! (CMK_HAS_TLS_VARIABLES && !CMK_NOT_USE_TLS_THREAD)
00483 pthread_key_create(&Cmi_state_key, 0);
00484 Cmi_state_vector =
00485 (CmiState)calloc(_Cmi_mynodesize+1, sizeof(struct CmiStateStruct));
00486 for (i=0; i<_Cmi_mynodesize; i++)
00487 CmiStateInit(i+Cmi_nodestart, i, CmiGetStateN(i));
00488
00489
00490 CmiStateInit(_Cmi_mynode+CmiNumPes(),_Cmi_mynodesize,CmiGetStateN(_Cmi_mynodesize));
00491 #else
00492
00493 Cmi_state_vector = (CmiState *)calloc(_Cmi_mynodesize+1, sizeof(CmiState));
00494 #if CMK_CONVERSE_MPI
00495
00496 if(!CharmLibInterOperate) {
00497 CmiStateInit(_Cmi_mynode+CmiNumPes(), _Cmi_mynodesize, &Cmi_mystate);
00498 Cmi_state_vector[_Cmi_mynodesize] = &Cmi_mystate;
00499 } else
00500 #endif
00501 {
00502
00503 CmiStateInit(Cmi_nodestart, 0, &Cmi_mystate);
00504 Cmi_state_vector[0] = &Cmi_mystate;
00505 }
00506 #endif
00507
00508 #if CMK_MULTICORE || CMK_SMP_NO_COMMTHD
00509 if (!Cmi_commthread)
00510 tocreate = _Cmi_mynodesize-1;
00511 else
00512 #endif
00513 tocreate = _Cmi_mynodesize;
00514 #if CMK_CONVERSE_MPI
00515 if(!CharmLibInterOperate) {
00516 start = 0;
00517 end = tocreate - 1;
00518 } else
00519 #endif
00520 {
00521 start = 1;
00522 end = tocreate;
00523 }
00524
00525 #if CMK_CONVERSE_PAMI
00526
00527 _Cmi_mypidlist = (pthread_t *)malloc(sizeof(pthread_t)*(end - start +1));
00528 int numThreads = 0;
00529 #endif
00530
00531 for (i=start; i<=end; i++) {
00532 pthread_attr_init(&attr);
00533 pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
00534 ok = pthread_create(&pid, &attr, call_startfn, (void *)i);
00535 if (ok!=0){
00536 CmiPrintf("CmiStartThreads: %s(%d)\n", strerror(errno), errno);
00537 PerrorExit("pthread_create");
00538 }
00539 #if CMK_CONVERSE_PAMI
00540 _Cmi_mypidlist[numThreads++] = pid;
00541 #endif
00542 pthread_attr_destroy(&attr);
00543 }
00544 #if ! (CMK_HAS_TLS_VARIABLES && !CMK_NOT_USE_TLS_THREAD)
00545 #if CMK_CONVERSE_MPI
00546 if(!CharmLibInterOperate)
00547 pthread_setspecific(Cmi_state_key, Cmi_state_vector+_Cmi_mynodesize);
00548 else
00549 #endif
00550 pthread_setspecific(Cmi_state_key, Cmi_state_vector);
00551 #endif
00552
00553 MACHSTATE(4,"CmiStartThreads done")
00554 }
00555
00556 static void CmiDestroyLocks(void)
00557 {
00558 CmiDestroyLock(comm_mutex);
00559 comm_mutex = 0;
00560 CmiDestroyLock(CmiMemLock_lock);
00561 CmiMemLock_lock = 0;
00562 pthread_mutex_destroy(&barrier_mutex);
00563 #ifdef CMK_NO_ASM_AVAILABLE
00564 CmiDestroyLock(cmiMemoryLock);
00565 #endif
00566 }
00567
00568 #endif
00569
00570 #if !CMK_SHARED_VARS_UNAVAILABLE
00571
00572
00573 void CmiNodeBarrier(void) {
00574 CmiNodeBarrierCount(CmiMyNodeSize(), CMI_NODE_BARRIER);
00575 }
00576
00577
00578
00579
00580 void CmiNodeAllBarrier(void) {
00581 #if CMK_MULTICORE || CMK_SMP_NO_COMMTHD
00582 if (!Cmi_commthread)
00583 CmiNodeBarrierCount(CmiMyNodeSize(), CMI_NODE_BARRIER);
00584 else
00585 #endif
00586 CmiNodeBarrierCount(CmiMyNodeSize()+1, CMI_NODE_ALL_BARRIER);
00587 }
00588
00589 #endif
00590
00591
00592
00593
00594
00595
00596
00597
00598 static int CmiIdleLock_hasMessage(CmiState cs) {
00599 return cs->idle.hasMessages;
00600 }
00601
00602 #if CMK_SHARED_VARS_NT_THREADS
00603
00604 static void CmiIdleLock_init(CmiIdleLock *l) {
00605 l->hasMessages=0;
00606 l->isSleeping=0;
00607 l->sem=CreateSemaphore(NULL,0,1, NULL);
00608 }
00609
00610 static void CmiIdleLock_sleep(CmiIdleLock *l,int msTimeout) {
00611 if (l->hasMessages) return;
00612 l->isSleeping=1;
00613 MACHSTATE(4,"Processor going to sleep {")
00614 WaitForSingleObject(l->sem,msTimeout);
00615 MACHSTATE(4,"} Processor awake again")
00616 l->isSleeping=0;
00617 }
00618
00619 static void CmiIdleLock_addMessage(CmiIdleLock *l) {
00620 l->hasMessages=1;
00621 if (l->isSleeping) {
00622 MACHSTATE(4,"Waking sleeping processor")
00623 ReleaseSemaphore(l->sem,1,NULL);
00624 }
00625 }
00626 static void CmiIdleLock_checkMessage(CmiIdleLock *l) {
00627 l->hasMessages=0;
00628 }
00629
00630 #elif CMK_SHARED_VARS_POSIX_THREADS_SMP
00631
00632 static void CmiIdleLock_init(CmiIdleLock *l) {
00633 l->hasMessages=0;
00634 l->isSleeping=0;
00635 pthread_mutex_init(&l->mutex,NULL);
00636 pthread_cond_init(&l->cond,NULL);
00637 }
00638
00639 #include <sys/time.h>
00640
00641 static void getTimespec(int msFromNow,struct timespec *dest) {
00642 struct timeval cur;
00643 int secFromNow;
00644
00645 gettimeofday(&cur,NULL);
00646 dest->tv_sec=cur.tv_sec;
00647 dest->tv_nsec=cur.tv_usec*1000;
00648
00649 secFromNow=msFromNow/1000;
00650 msFromNow-=secFromNow*1000;
00651 dest->tv_sec+=secFromNow;
00652 dest->tv_nsec+=1000*1000*msFromNow;
00653
00654 while (dest->tv_nsec>=1000000000ul) {
00655 dest->tv_nsec-=1000000000ul;
00656 dest->tv_sec++;
00657 }
00658 }
00659
00660 static void CmiIdleLock_sleep(CmiIdleLock *l,int msTimeout) {
00661 struct timespec wakeup;
00662
00663 if (l->hasMessages) return;
00664 l->isSleeping=1;
00665 MACHSTATE(4,"Processor going to sleep {")
00666 pthread_mutex_lock(&l->mutex);
00667 getTimespec(msTimeout,&wakeup);
00668 while (!l->hasMessages)
00669 if (ETIMEDOUT==pthread_cond_timedwait(&l->cond,&l->mutex,&wakeup))
00670 break;
00671 pthread_mutex_unlock(&l->mutex);
00672 MACHSTATE(4,"} Processor awake again")
00673 l->isSleeping=0;
00674 }
00675
00676 static void CmiIdleLock_wakeup(CmiIdleLock *l) {
00677 l->hasMessages=1;
00678 MACHSTATE(4,"Waking sleeping processor")
00679
00680 pthread_mutex_lock(&l->mutex);
00681 pthread_cond_signal(&l->cond);
00682 pthread_mutex_unlock(&l->mutex);
00683 }
00684
00685 static void CmiIdleLock_addMessage(CmiIdleLock *l) {
00686 if (l->isSleeping) CmiIdleLock_wakeup(l);
00687 l->hasMessages=1;
00688 }
00689 static void CmiIdleLock_checkMessage(CmiIdleLock *l) {
00690 l->hasMessages=0;
00691 }
00692 #else
00693 #define CmiIdleLock_sleep(x, y)
00694
00695 static void CmiIdleLock_init(CmiIdleLock *l) {
00696 l->hasMessages=0;
00697 }
00698 static void CmiIdleLock_addMessage(CmiIdleLock *l) {
00699 l->hasMessages=1;
00700 }
00701 static void CmiIdleLock_checkMessage(CmiIdleLock *l) {
00702 l->hasMessages=0;
00703 }
00704 #endif
00705
00706 void CmiStateInit(int pe, int rank, CmiState state)
00707 {
00708 #if CMK_SMP_MULTIQ
00709 int i;
00710 #endif
00711
00712 MACHSTATE(4,"StateInit")
00713 state->pe = pe;
00714 state->rank = rank;
00715 if (rank==CmiMyNodeSize()) return;
00716 #if !CMK_SMP_MULTIQ
00717 state->recv = CMIQueueCreate();
00718 #else
00719 for(i=0; i<MULTIQ_GRPSIZE; i++) state->recv[i]=CMIQueueCreate();
00720 state->myGrpIdx = rank % MULTIQ_GRPSIZE;
00721 state->curPolledIdx = 0;
00722 #endif
00723 state->localqueue = CdsFifo_Create();
00724 CmiIdleLock_init(&state->idle);
00725 }
00726
00727 void CmiNodeStateInit(CmiNodeState *nodeState)
00728 {
00729 MACHSTATE1(4,"NodeStateInit %p", nodeState)
00730 #if CMK_IMMEDIATE_MSG
00731 nodeState->immSendLock = CmiCreateLock();
00732 nodeState->immRecvLock = CmiCreateLock();
00733 nodeState->immQ = CMIQueueCreate();
00734 nodeState->delayedImmQ = CMIQueueCreate();
00735 #endif
00736 #if CMK_NODE_QUEUE_AVAILABLE
00737 nodeState->CmiNodeRecvLock = CmiCreateLock();
00738 #if CMK_LOCKLESS_QUEUE
00739 nodeState->NodeRecv = MPMCQueueCreate();
00740 #else
00741 nodeState->NodeRecv = CMIQueueCreate();
00742 #endif
00743 #endif
00744 MACHSTATE(4,"NodeStateInit done")
00745 }
00746